{
  "schema_version": "0.3",
  "generated": "2026-09-24",
  "base_url": "https://trove.xin",
  "routing": {
    "human": {
      "zh": "https://trove.xin/",
      "en": "https://trove.xin/en/"
    },
    "agent": {
      "index": "https://trove.xin/agent/",
      "data": "https://trove.xin/agent/trove.json",
      "llms": "https://trove.xin/llms.txt"
    }
  },
  "summary": {
    "tools": 29,
    "problems": 159,
    "experience_entries": 13,
    "domains": 11,
    "verified_conflict_pairs": 0,
    "conflict_pairs": 0,
    "verified_tools": 29,
    "stale_tools": 0,
    "candidate_tools": 0,
    "combos": 16
  },
  "tools": [
    {
      "name": "agent-framework",
      "slug": "agent-framework",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.16.0",
        "max": "1.16.0"
      },
      "license": {},
      "install": {
        "cmd": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework && python3 -c \"import agent_framework\"",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip 安装 meta 包 agent-framework（2026-08-31 本环境 venv 复测（1.16.0），Python 3.14，清华镜像；PyPI 官方直连超时）；meta 包依赖 agent-framework-core（核心库，import agent_framework 即来自 core），完整链路还有 agent-framework-ext 等子包（官方支持 Python 3.10-3.13；本环境 3.14 部分依赖无预编译轮子，全量安装超时，故以 core + 核心依赖实测）；库形态无 CLI 二进制；完整跑通需 LLM provider（Azure Foundry/OpenAI/Anthropic 客户端）"
      },
      "writes": [],
      "runtime": [],
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "summary": "多 Agent 构建/编排/部署框架（Python 库，微软出品，AutoGen 演进）",
      "capabilities": [
        "多 Agent 编排",
        "顺序/并行工作流",
        "A2A 协议",
        "AG-UI 交互",
        "Azure Foundry/OpenAI/Anthropic 客户端",
        "记忆与技能抽象"
      ],
      "validated_at": "2026-08-31",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "superpowers",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "code-review-graph",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "logfire",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "moltis",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-framework-2",
          "title": "Python 3.14 装 agent-framework 全量卡死：resolution-too-deep（依赖图过深）"
        },
        {
          "id": "agent-framework-3",
          "title": "MCP 连接只显示 Cancelled via...（真实 401 认证错误被掩盖）"
        },
        {
          "id": "agent-framework-4",
          "title": "agent hooks 装不上/不生效（仓库 setup 只做 git hooks）"
        },
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "pip-mirror-source",
          "title": "pip 安装超时/下载慢（ReadTimeoutError / WARNING: Retrying ... Read timed out）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/agent-framework",
        "human_en": "https://trove.xin/en/solutions/agent-framework",
        "agent": "https://trove.xin/agent/solutions/agent-framework",
        "detailed": "https://trove.xin/detailed/agent-framework"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework\npython3 -c \"import agent_framework; print('agent_framework OK', agent_framework.__file__)\"",
          "expect": "预期输出: agent_framework OK .../site-packages/agent_framework/__init__.py"
        },
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework\npython3 -c \"import agent_framework; print('agent_framework OK', agent_framework.__file__)\"",
          "expect": "预期输出: agent_framework OK .../site-packages/agent_framework/__init__.py"
        },
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework-core==1.16.0\npython3 -c \"from agent_framework import Agent; print('Agent class OK')\"",
          "expect": "预期输出: Agent class OK"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import agent_framework; print([a for a in dir(agent_framework) if not a.startswith('_')][:20])\"",
          "expect": "预期输出: Agent/AgentContext/BaseAgent/BaseChatClient 等编排抽象"
        },
        {
          "lang": "agent",
          "code": "python3 - <<'PY'\nimport asyncio\nfrom agent_framework import Agent\nfrom agent_framework.azure import AzureChatClient  # 或 OpenAI/Anthropic 客户端\n# 需要 FOUNDRY_PROJECT_ENDPOINT / FOUNDRY_MODEL_DEPLOYMENT_NAME 或等价环境变量\nasync def main:\n    agent = Agent(client=AzureChatClient, name=\"Demo\", instructions=\"You are helpful.\")\n    print(await agent.run(\"hi\"))\nasyncio.run(main)\nPY",
          "expect": "预期输出: 模型回复（依赖 provider 配置）"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import agent_framework; print('agent_framework OK', agent_framework.__file__)\"",
          "expect": "预期输出: agent_framework OK .../site-packages/agent_framework/__init__.py"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"from agent_framework import Agent; print('Agent class OK')\"",
          "expect": "预期输出: Agent class OK"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"from agent_framework import Agent; print('Agent class OK')\"",
          "expect": "预期输出: Agent class OK"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import agent_framework; print([a for a in dir(agent_framework) if not a.startswith('_')][:20])\"",
          "expect": "预期输出: Agent/AgentContext/BaseAgent/BaseChatClient 等编排抽象"
        },
        {
          "lang": "agent",
          "code": "python3 - <<'PY'\nimport asyncio\nfrom agent_framework import Agent\nfrom agent_framework.azure import AzureChatClient  # 或 OpenAI/Anthropic 客户端\n# 需要 FOUNDRY_PROJECT_ENDPOINT / FOUNDRY_MODEL_DEPLOYMENT_NAME 或等价环境变量\nasync def main:\n    agent = Agent(client=AzureChatClient, name=\"Demo\", instructions=\"You are helpful.\")\n    print(await agent.run(\"hi\"))\nasyncio.run(main)\nPY",
          "expect": "预期输出: 模型回复（依赖 provider 配置）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y agent-framework agent-framework-core agent-framework-ext 2>/dev/null || true\npython3 -c \"import importlib.util; print('残留: agent_framework 仍存在' if importlib.util.find_spec('agent_framework') else 'agent-framework removed: OK')\"",
          "expect": "预期输出: agent-framework removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-31",
        "ttl_due": "2027-02-27",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.16.0",
          "max": "1.16.0"
        }
      }
    },
    {
      "name": "agent-skills",
      "slug": "agent-skills",
      "category": "skills",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.6.7",
        "max": "0.6.7"
      },
      "license": {},
      "install": {
        "cmd": "npx -y skills add addyosmani/agent-skills --yes --global 2>/dev/null || (curl -sL https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz && cp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills)",
        "interactive": false,
        "semi_auto_note": "实测命令 = skills CLI（vercel-labs/skills，npx skills add addyosmani/agent-skills；本环境 GitHub clone 慢超时，改用 codeload tarball 1.9s 取到 24 个 SKILL.md + 8 个斜杠命令，结构确认）；官方快速路径 = npx skills add addyosmani/agent-skills（装到 70+ agent，含 Claude Code/Cursor/Codex/Copilot）；手工路径 = 复制 skills/ 目录；写面 = 技能目录（~/.claude/skills 或项目 .claude/skills，与 superpowers 同面）"
      },
      "writes": [],
      "runtime": [],
      "domain": "skills",
      "domain_label": "技能管理",
      "summary": "生产级工程技能集（24 技能 + 8 斜杠命令；定义/规划/构建/测试/审查/上线全流程）",
      "capabilities": [
        "24 个工程技能（SKILL.md）",
        "8 个生命周期斜杠命令",
        "70+ agent 兼容（skills CLI）",
        "技能自动激活",
        "build auto 自主执行"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-skills-1",
          "title": "官方 CLI 安装失败：GitHub clone 被重置/卡死（国内高频）"
        },
        {
          "id": "agent-skills-2",
          "title": "安装失败排查：Node 版本 / pnpm / npm 缓存"
        },
        {
          "id": "agent-skills-3",
          "title": "同名项目污染：pip 装到 ivanzwb / 国外多个同名 repo"
        },
        {
          "id": "agent-skills-4",
          "title": "装完看不到技能 / document-skills 安装问答"
        },
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/agent-skills",
        "human_en": "https://trove.xin/en/solutions/agent-skills",
        "agent": "https://trove.xin/agent/solutions/agent-skills",
        "detailed": "https://trove.xin/detailed/agent-skills"
      },
      "guide_blocks": [
        {
          "lang": "bash",
          "code": "npx -y skills add addyosmani/agent-skills --yes --global 2>/dev/null || (curl -sL https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz && cp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills)",
          "expect": "预期输出: 技能就位，共 24 个（注意两种方式布局不同：方式 A 平铺为 ~/.claude/skills/<技能名>/，方式 B 带中间层 ~/.claude/skills/agent-skills/——Claude Code 两种布局都读取）"
        },
        {
          "lang": "bash",
          "code": "curl -sL https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz\ncp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills",
          "expect": "预期输出: 无报错；~/.claude/skills/agent-skills/ 下出现 24 个技能目录"
        },
        {
          "lang": "bash",
          "code": "# 方式 A（平铺）：单独装本包时 ~/.claude/skills/ 下 24 个技能目录\nls ~/.claude/skills | wc -l\n# 方式 B（中间层）：\nls ~/.claude/skills/agent-skills | wc -l",
          "expect": "预期输出: 24"
        },
        {
          "lang": "bash",
          "code": "/spec    # 需求→规格（Spec before code）\n/plan    # 规格→原子任务\n/build   # 增量实现（/build auto 一键规划+实现）\n/test    # 测试即证据\n/review  # 合并前审查\n/ship    # 上线",
          "expect": "预期输出: 对应工作流激活（自动装载相关技能）"
        },
        {
          "lang": "bash",
          "code": "# 设计 API 时自动激活 api-and-interface-design；改 UI 时激活 frontend-ui-engineering 等",
          "expect": "预期输出: agent 上下文自动注入对应技能"
        },
        {
          "lang": "agent",
          "code": "# 方式 A（平铺）\nls ~/.claude/skills | wc -l\n# 方式 B（中间层）\nls ~/.claude/skills/agent-skills | wc -l",
          "expect": "预期输出: 24（单独装本包时）"
        },
        {
          "lang": "agent",
          "code": "/spec    # 需求→规格（Spec before code）\n/plan    # 规格→原子任务\n/build   # 增量实现（/build auto 一键规划+实现）\n/test    # 测试即证据\n/review  # 合并前审查\n/ship    # 上线",
          "expect": "预期输出: 对应工作流激活（自动装载相关技能）"
        },
        {
          "lang": "agent",
          "code": "curl -sL https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz\ncp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills",
          "expect": "预期输出: 无报错；技能目录刷新（24 个技能目录）"
        },
        {
          "lang": "agent",
          "code": "# 方式 A（skills CLI 安装）：从 CLI 卸载\nnpx -y skills remove addyosmani/agent-skills\n# 方式 B（手工复制）：删目录\nrm -rf ~/.claude/skills/agent-skills\n# 验证（两种方式通用）\ntest ! -d ~/.claude/skills/agent-skills && echo \"agent-skills removed: OK\"",
          "expect": "预期输出: agent-skills removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.6.7",
          "max": "0.6.7"
        }
      }
    },
    {
      "name": "agentshield",
      "slug": "agentshield",
      "category": "security",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.4.0",
        "max": "1.4.0"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npx -y ecc-agentshield scan --no-report",
        "interactive": false,
        "semi_auto_note": "实测命令 = npx ecc-agentshield scan（2026-08-28 本环境验证，npm 包 ecc-agentshield 1.5.0，npmmirror）；GitHub release v1.4.0（2026-03-22）无资产，官方分发走 npm；扫描 = 只读（实测无写面）；无后台进程；npm 包内有全局安装形态（npm i -g ecc-agentshield）"
      },
      "writes": [],
      "runtime": [],
      "domain": "security",
      "domain_label": "安全与治理",
      "summary": "Agent 配置安全审计器：扫描 Claude Code 配置中的密钥、权限、钩子、MCP 与提示注入面",
      "capabilities": [
        "密钥/凭据扫描",
        "权限配置审计",
        "钩子注入检测",
        "MCP 服务器风险评估",
        "提示注入面检查",
        "分级安全报告"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-security-preflight",
          "title": "安全前置（agentshield + skillspector）",
          "page": "https://trove.xin/combos/combo-security-preflight"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/agentshield",
        "human_en": "https://trove.xin/en/solutions/agentshield",
        "agent": "https://trove.xin/agent/solutions/agentshield",
        "detailed": "https://trove.xin/detailed/agentshield"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npx -y ecc-agentshield scan",
          "expect": "预期输出: AgentShield Security Report（目标 = 自动发现的 ~/.claude/ 或当前目录；分级报告 + 发现清单）"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npx -y ecc-agentshield scan",
          "expect": "预期输出: AgentShield Security Report（目标 = 自动发现的 ~/.claude/ 或当前目录；分级报告 + 发现清单）"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g ecc-agentshield\nagentshield scan",
          "expect": "预期输出: 同上（Security Report）"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan /path/to/.claude",
          "expect": "预期输出: 安全报告（Secrets/Permissions/Hooks/MCP Servers/Agents 分项 + 文件级发现）"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan -f json",
          "expect": "预期输出: JSON 报告（实测字段：timestamp / targetPath / findings[]（id/severity/category/title/file/fix）/ score（grade/numericScore/breakdown）/ summary（totalFindings/critical/high/medium/low/info/filesScanned））"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan --save-baseline\nnpx -y ecc-agentshield scan --baseline --gate",
          "expect": "预期输出: 首次扫描存基线（Baseline saved）；之后扫描对比基线，出现新发现时输出 `Gate: FAILED`（实测：基线 2 发现 → 引入新 secret → Gate FAILED）——关键坑：gate 失败时进程退出码仍为 0，CI 必须解析输出文本（Gate: FAILED）或 JSON summary，不能只靠退出码"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield --version",
          "expect": "预期输出: 1.5.0（本环境实测——CLI 自报版本；npm 包版本为 1.4.0，见依赖警告）"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan",
          "expect": "预期输出: Security Report（分级 + 发现清单；扫描只读，跑多少次都不改配置）"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield --version",
          "expect": "预期输出: 1.5.0"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan /path/to/.claude",
          "expect": "预期输出: 安全报告（Secrets/Permissions/Hooks/MCP Servers/Agents 分项 + 文件级发现）"
        },
        {
          "lang": "agent",
          "code": "npx -y ecc-agentshield scan -f json",
          "expect": "预期输出: JSON 报告（实测字段：timestamp / targetPath / findings[]（id/severity/category/title/file/fix）/ score（grade/numericScore/breakdown）/ summary（totalFindings/critical/high/medium/low/info/filesScanned））"
        },
        {
          "lang": "agent",
          "code": "npm rm -g ecc-agentshield 2>/dev/null || true\nnpm ls -g ecc-agentshield 2>/dev/null | grep -q ecc-agentshield && echo '残留: ecc-agentshield 仍存在' || echo 'agentshield removed: OK'",
          "expect": "预期输出: agentshield removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.4.0",
          "max": "1.4.0"
        }
      }
    },
    {
      "name": "agent-governance-toolkit",
      "slug": "agent-governance-toolkit",
      "category": "gate",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "4.1.0",
        "max": "4.1.0"
      },
      "license": {},
      "install": {
        "cmd": "pip install agent-governance-toolkit && agt --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip 安装 meta 包（官方 PyPI，2026-08-27 本环境 venv 验证，agt 4.1.0）；国内可加 --index-url https://pypi.tuna.tsinghua.edu.cn/simple（见指南安装段地区分支）；[full] extras 全家桶与 Claude Code 插件路径见指南"
      },
      "writes": [],
      "runtime": [],
      "domain": "security",
      "domain_label": "安全与治理",
      "summary": "AI Agent 治理工具包：策略执行、零信任身份与执行沙箱，适合需要给 AI Agent 上治理与合规边界的团队",
      "capabilities": [
        "策略门禁",
        "合规验证（OWASP ASI）",
        "零信任身份",
        "执行沙箱",
        "SRE"
      ],
      "validated_at": "2026-08-27",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "Evidence details kept locally"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "claude-json-multi-tool",
          "title": "多个工具都写 ~/.claude.json，一起装会冲突吗？（实测兼容）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-governance-stack",
          "title": "开发链路治理栈（agent-governance-toolkit + code-review-graph + herdr）",
          "page": "https://trove.xin/combos/combo-governance-stack"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/agent-governance-toolkit",
        "human_en": "https://trove.xin/en/solutions/agent-governance-toolkit",
        "agent": "https://trove.xin/agent/solutions/agent-governance-toolkit",
        "detailed": "https://trove.xin/detailed/agent-governance-toolkit"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install agent-governance-toolkit --index-url https://pypi.tuna.tsinghua.edu.cn/simple\nagt --version",
          "expect": "预期输出: agt, version 4.1.0"
        },
        {
          "lang": "agent",
          "code": "pip install agent-governance-toolkit\nagt --version",
          "expect": "预期输出: agt, version 4.1.0"
        },
        {
          "lang": "agent",
          "code": "pip install agent-governance-toolkit --index-url https://pypi.tuna.tsinghua.edu.cn/simple\nagt --version",
          "expect": "预期输出: agt, version 4.1.0"
        },
        {
          "lang": "agent",
          "code": "agt doctor",
          "expect": "预期输出: AGT Doctor 开始，列出已安装包与配置路径"
        },
        {
          "lang": "agent",
          "code": "agt lint-policy ./policies",
          "expect": "预期输出: No issues found.（有格式问题时逐行报 error）"
        },
        {
          "lang": "agent",
          "code": "agt --version",
          "expect": "预期输出: agt, version 4.1.0"
        },
        {
          "lang": "agent",
          "code": "agt doctor",
          "expect": "预期输出: AGT Doctor 开始，列出已安装包与配置路径（无红色缺失项）"
        },
        {
          "lang": "agent",
          "code": "agt lint-policy ./policies",
          "expect": "预期输出: No issues found.（有格式问题时逐行报 error）"
        },
        {
          "lang": "agent",
          "code": "agt --help",
          "expect": "预期输出: 以 Usage: agt [OPTIONS] COMMAND [ARGS]... 开头，含 verify/doctor/lint-policy/test/integrity/red-team/cred"
        },
        {
          "lang": "agent",
          "code": "agt doctor",
          "expect": "预期输出: AGT Doctor 开始，列出已安装包与配置路径"
        },
        {
          "lang": "agent",
          "code": "agt lint-policy ./policies",
          "expect": "预期输出: No issues found.（有格式问题时逐行报 error）"
        },
        {
          "lang": "agent",
          "code": "agt verify",
          "expect": "预期输出: 合规检查结果（策略未配置时给出引导提示）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y agent-governance-toolkit || true\ncommand -v agt >/dev/null 2>&1 && echo \"agt 仍存在（方式 C 安装请执行 /plugin remove agent-governance-toolkit）\" || echo \"agt removed: OK\"",
          "expect": "预期输出: agt removed: OK"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y agent-os-kernel agentmesh-platform agent-runtime agent-sre agentmesh-marketplace agentmesh-lightning agent-hypervisor",
          "expect": ""
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-27",
        "ttl_due": "2027-02-23",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "4.1.0",
          "max": "4.1.0"
        }
      }
    },
    {
      "name": "cc-switch",
      "slug": "cc-switch",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "3.20.1",
        "max": "3.20.1"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o cc-switch.deb https://gh-proxy.com/https://github.com/farion1231/cc-switch/releases/download/v3.20.1/CC-Switch-v3.20.1-Linux-x86_64.deb && sudo dpkg -i cc-switch.deb && cc-switch --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = deb 包结构确认（2026-08-28 本环境验证，v3.20.1，gh-proxy 加速 13.5MB；Tauri 桌面应用，二进制 31MB，依赖 libwebkit2gtk-4.1——本环境无 GUI 库未运行）；官方资产 19 个全平台（AppImage/deb/rpm/win/mac）；管理面 = ~/.claude.json（Claude Code provider）+ ~/.codex/config.toml（Codex）+ opencode 配置 + ~/.cc-switch/config.json（自身）+ SQLite（会话用量）；桌面 GUI 形态"
      },
      "writes": [],
      "runtime": [],
      "domain": "session",
      "domain_label": "会话与记忆",
      "summary": "跨平台 All-in-One 会话/供应商切换器（Claude Code/Codex/Gemini/OpenCode 等）",
      "capabilities": [
        "多 agent 供应商切换",
        "Claude Code/Codex 配置管理",
        "模型/API 配置切换",
        "会话用量统计（SQLite）",
        "技能/MCP 管理",
        "WebDAV 同步"
      ],
      "validated_at": "2026-08-31",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-mem",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "cc-switch-1",
          "title": "dpkg -i 依赖缺失：libwebkit2gtk 等（Linux 安装/启动失败）"
        },
        {
          "id": "cc-switch-2",
          "title": "GitHub Release 下载 TLS 断连 / 超时"
        },
        {
          "id": "cc-switch-3",
          "title": "架构不匹配（香橙派等 ARM 设备）"
        },
        {
          "id": "cc-switch-4",
          "title": "启动失败 / 白屏排查"
        },
        {
          "id": "cc-switch-5",
          "title": "安装来源混淆：npm 同名包 / 其它 cc-switch 项目"
        },
        {
          "id": "uninstall-leftover",
          "title": "卸载后残留——共享配置不还原 / 命令仍在"
        }
      ],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/cc-switch",
        "human_en": "https://trove.xin/en/solutions/cc-switch",
        "agent": "https://trove.xin/agent/solutions/cc-switch",
        "detailed": "https://trove.xin/detailed/cc-switch"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o cc-switch.AppImage https://gh-proxy.com/https://github.com/farion1231/cc-switch/releases/download/v3.20.1/CC-Switch-v3.20.1-Linux-x86_64.AppImage\nchmod +x cc-switch.AppImage && ./cc-switch.AppImage",
          "expect": "预期输出: 应用窗口（桌面环境）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o cc-switch.AppImage https://gh-proxy.com/https://github.com/farion1231/cc-switch/releases/download/v3.20.1/CC-Switch-v3.20.1-Linux-x86_64.AppImage\nchmod +x cc-switch.AppImage && ./cc-switch.AppImage",
          "expect": "预期输出: 应用窗口（桌面环境）"
        },
        {
          "lang": "agent",
          "code": "cp ~/.claude.json ~/.claude.json.bak-$(date +%F) 2>/dev/null || true\ncp ~/.codex/config.toml ~/.codex/config.toml.bak-$(date +%F) 2>/dev/null || true",
          "expect": "预期输出: 两个配置文件各有带日期的备份（如已有则跳过）"
        },
        {
          "lang": "agent",
          "code": "# GUI：配置面板选择目标供应商/模型 → 应用",
          "expect": "预期输出: ~/.claude.json 的 provider 配置被改写（切换生效；本环境未实测 GUI，依据官方文档）"
        },
        {
          "lang": "agent",
          "code": "# GUI：配置面板选择目标供应商/模型 → 应用",
          "expect": "预期输出: ~/.claude.json 的 provider 配置被改写（切换生效）"
        },
        {
          "lang": "agent",
          "code": "# GUI：Codex 面板添加供应商（OpenAI OAuth 或第三方）",
          "expect": "预期输出: ~/.codex/config.toml 被管理（OAuth token 过期提示重登）"
        },
        {
          "lang": "agent",
          "code": "# GUI：用量面板",
          "expect": "预期输出: 各 agent（Claude/Codex/Gemini/OpenCode 等）会话统计（SQLite 持久化）"
        },
        {
          "lang": "agent",
          "code": "sudo dpkg -r cc-switch 2>/dev/null || true\nrm -rf ~/.cc-switch\ncommand -v cc-switch >/dev/null 2>&1 && echo '残留: cc-switch 仍存在' || echo 'cc-switch removed: OK'",
          "expect": "预期输出: cc-switch removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-31",
        "ttl_due": "2027-02-27",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "3.20.1",
          "max": "3.20.1"
        }
      }
    },
    {
      "name": "claude-code-router",
      "slug": "claude-code-router",
      "category": "gate",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "3.0.22",
        "max": "3.0.22"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npm install -g @musistudio/claude-code-router --no-audit --no-fund && ccr --help",
        "interactive": false,
        "semi_auto_note": "实测命令 = npm i -g @musistudio/claude-code-router（2026-08-28 本环境验证，v3.0.22，9s/102 包）；国内用 npmmirror 前缀；首次运行需配置 provider（ccr 无模型时提示 No available models）；桌面端（release 二进制）本环境未实测；start/serve 会起网关进程（端口绑定），用 ccr stop 停止"
      },
      "writes": [],
      "runtime": [],
      "domain": "gateway",
      "domain_label": "网关与路由",
      "summary": "本地 Agent 控制面：Claude Code 等跨模型路由与供应商管理，适合同时用多家模型、想集中切换 API 的开发者",
      "capabilities": [
        "多模型/供应商路由",
        "网关服务（start/serve）",
        "Profile 路由切换",
        "浏览器管理界面",
        "官方中文文档"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "claude-code-router-1",
          "title": "EBADENGINE：Node 版本不满足 engines"
        },
        {
          "id": "claude-code-router-2",
          "title": "WSL 里 node: not found（npm 来自 Windows 侧）"
        },
        {
          "id": "claude-code-router-3",
          "title": "首次运行 No available models / 网关起不来"
        },
        {
          "id": "claude-code-router-4",
          "title": "本地模型 api_base_url 配置错误（Ollama 等）"
        },
        {
          "id": "claude-code-router-5",
          "title": "Docker 部署：端口 / volume / 权限"
        },
        {
          "id": "claude-code-router-6",
          "title": "JSON / API 配置错误导致服务启动失败"
        },
        {
          "id": "uninstall-leftover",
          "title": "卸载后残留——共享配置不还原 / 命令仍在"
        }
      ],
      "related_combos": [
        {
          "id": "combo-multi-agent-workflow",
          "title": "多 Agent 工作流编排",
          "page": "https://trove.xin/combos/combo-multi-agent-workflow"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/claude-code-router",
        "human_en": "https://trove.xin/en/solutions/claude-code-router",
        "agent": "https://trove.xin/agent/solutions/claude-code-router",
        "detailed": "https://trove.xin/detailed/claude-code-router"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g @musistudio/claude-code-router --no-audit --no-fund\nccr --help",
          "expect": "预期输出: ccr 用法（start/ui/serve/stop/profile 路由）"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g @musistudio/claude-code-router --no-audit --no-fund\nccr --help",
          "expect": "预期输出: ccr 用法（start/ui/serve/stop/profile 路由）"
        },
        {
          "lang": "agent",
          "code": "ccr start --no-open",
          "expect": "预期输出: 网关启动状态；浏览器管理界面地址（--no-open 不开浏览器）"
        },
        {
          "lang": "agent",
          "code": "ccr default-codex -- --model gpt-5-codex",
          "expect": "预期输出: 以指定 profile 启动 Codex（需先配置该 profile）"
        },
        {
          "lang": "agent",
          "code": "ccr --help",
          "expect": "预期输出: ccr start / ccr ui / ccr serve / ccr stop / ccr <profile> [cli|app]（子命令齐全）"
        },
        {
          "lang": "agent",
          "code": "ccr stop",
          "expect": "预期输出: 网关已停止（能正常停止 = 网关进程可管理）"
        },
        {
          "lang": "agent",
          "code": "ccr start --no-open",
          "expect": "预期输出: 网关启动状态；浏览器管理界面地址（--no-open 不开浏览器）"
        },
        {
          "lang": "agent",
          "code": "ccr default-codex -- --model gpt-5-codex",
          "expect": "预期输出: 以指定 profile 启动 Codex（需先配置该 profile）"
        },
        {
          "lang": "agent",
          "code": "ccr stop",
          "expect": "预期输出: 网关已停止"
        },
        {
          "lang": "agent",
          "code": "ccr --help",
          "expect": "预期输出: ccr start / ccr ui / ccr serve / ccr stop / ccr <profile> [cli|app]"
        },
        {
          "lang": "agent",
          "code": "npm rm -g @musistudio/claude-code-router || true\nrm -rf ~/.claude-code-router\ncommand -v ccr >/dev/null 2>&1 && echo \"残留: ccr 仍存在\" || echo \"claude-code-router removed: OK\"",
          "expect": "预期输出: claude-code-router removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "3.0.22",
          "max": "3.0.22"
        }
      }
    },
    {
      "name": "claude-mem",
      "slug": "claude-mem",
      "category": "retro",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "13.18.0",
        "max": "13.18.0"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem && claude-mem install --ide claude-code",
        "interactive": false,
        "semi_auto_note": "实测命令 = npm 全局装 claude-mem CLI（12.4.7）→ claude-mem install --ide claude-code（2026-08-28 隔离 HOME 验证；交互提示用默认应答即完成）；插件本体版本 13.16.1（GitHub release tag 同步；npm CLI 版本号滞后为 12.4.7）；安装写面 = ~/.claude.json（插件注册）+ ~/.claude/plugins/（marketplaces/cache）+ ~/.claude/backups/ + ~/.claude-mem/（settings/logs/记忆存储）；worker 端口 37700（记忆 UI + MCP mcp-search）；runtime 需 Bun（本环境已装）"
      },
      "writes": [],
      "runtime": [],
      "domain": "session",
      "domain_label": "会话与记忆",
      "summary": "跨会话持久记忆：捕获会话、AI 压缩并注入相关上下文（MCP + worker），适合想让 Claude Code 记住上下文的开发者",
      "capabilities": [
        "跨会话记忆",
        "会话捕获（transcript watch）",
        "记忆搜索（/mem-search）",
        "AI 上下文压缩",
        "worktree 记忆整合（adopt）",
        "本地记忆 UI（端口 37700）"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "code-review-graph",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "cc-switch",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "claude-mem-1",
          "title": "marketplace 克隆失败：SSH 认证失败 / 克隆卡死"
        },
        {
          "id": "claude-mem-2",
          "title": "worker/UI 端口占用：\"Is port 37777 in use?\""
        },
        {
          "id": "claude-mem-3",
          "title": "首会话重复 SessionStart hook 错误（worker 竞态）"
        },
        {
          "id": "claude-mem-4",
          "title": "npm 依赖安装失败"
        },
        {
          "id": "claude-mem-5",
          "title": "Chroma 进程泄漏 / zombie 链"
        },
        {
          "id": "claude-mem-6",
          "title": "第三方模型鉴权切换后 \"No previous sessions found\""
        },
        {
          "id": "claude-mem-7",
          "title": "opencode 安装 API 不匹配"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        },
        {
          "id": "combo-memory-session",
          "title": "单会话记忆治理栈（claude-mem + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-memory-session"
        },
        {
          "id": "combo-multi-agent-workflow",
          "title": "多 Agent 工作流编排",
          "page": "https://trove.xin/combos/combo-multi-agent-workflow"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/claude-mem",
        "human_en": "https://trove.xin/en/solutions/claude-mem",
        "agent": "https://trove.xin/agent/solutions/claude-mem",
        "detailed": "https://trove.xin/detailed/claude-mem"
      },
      "guide_blocks": [
        {
          "lang": "bash",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem && claude-mem install --ide claude-code",
          "expect": "预期输出: Installation Complete（Plugin dir: ~/.claude/plugins/marketplaces/thedotmack；Worker port: 37700）"
        },
        {
          "lang": "bash",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem\nclaude-mem install --ide claude-code",
          "expect": "预期输出: Installation Complete（Plugin dir: ~/.claude/plugins/marketplaces/thedotmack；Worker port: 37700）"
        },
        {
          "lang": "bash",
          "code": "claude-mem status",
          "expect": "预期输出: Worker is not running（已 install、未启动时）；未 install 时输出 `claude-mem is not installed. Run: npx claude-mem install`（2026-08-29 实测：CLI 12.4.7 为引导器，runtime 命令依赖 Bun）；启动后显示端口与记忆数"
        },
        {
          "lang": "bash",
          "code": "claude-mem start",
          "expect": "预期输出: worker 启动（记忆 UI http://localhost:37700；Claude Code 内 /mem-search 可用）"
        },
        {
          "lang": "bash",
          "code": "claude-mem status              # 看 worker 状态与记忆数\nclaude-mem search \"部署问题\"    # 主动搜记忆\nclaude-mem adopt --dry-run     # worktree 记忆整合（合并分支后；确认后去掉 --dry-run）\nclaude-mem stop                # 停止 worker",
          "expect": "预期输出: status 显示端口与记忆数；search 返回相关记忆片段列表；adopt --dry-run 列出待整合清单；stop 输出 worker stopped"
        },
        {
          "lang": "agent",
          "code": "claude-mem status",
          "expect": "预期输出: 运行中显示端口与记忆数；已安装未启动时显示 Worker is not running"
        },
        {
          "lang": "agent",
          "code": "claude-mem status              # 看 worker 状态与记忆数\nclaude-mem search \"部署问题\"    # 主动搜记忆\nclaude-mem adopt --dry-run     # worktree 记忆整合（合并分支后；确认后去掉 --dry-run）\nclaude-mem stop                # 停止 worker",
          "expect": "预期输出: status 显示端口与记忆数；search 返回相关记忆片段列表；adopt --dry-run 列出待整合清单；stop 输出 worker stopped"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "13.18.0",
          "max": "13.18.0"
        }
      }
    },
    {
      "name": "code-review-graph",
      "slug": "code-review-graph",
      "category": "gate",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "2.3.8",
        "max": "2.3.8"
      },
      "license": {},
      "install": {
        "cmd": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8 && code-review-graph --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip install -i 清华镜像 code-review-graph==2.3.8（2026-08-28 本环境验证，隔离 venv，Python 3.14）；国内必须带清华镜像（PyPI 官方超时）；可选 pipx 安装（官方推荐，本机未实测）；code-review-graph install 会向 Claude/Codex/Cursor/OpenCode 注册 MCP/hooks（写 ~/.claude.json、~/.codex/hooks.json、工作区 .mcp.json 等，见 guide 共享面）；crg-daemon 为后台进程"
      },
      "writes": [],
      "runtime": [],
      "domain": "review",
      "domain_label": "代码审查",
      "summary": "本地代码知识图谱：给 AI 代码审查只喂相关文件，省 token、上下文聚焦，适合大型代码库的 AI 审查",
      "capabilities": [
        "tree-sitter 代码图谱",
        "MCP 集成",
        "增量更新（watch）",
        "死代码/影响分析",
        "官方中文文档"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "superpowers",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "claude-json-multi-tool",
          "title": "多个工具都写 ~/.claude.json，一起装会冲突吗？（实测兼容）"
        },
        {
          "id": "code-review-graph-2",
          "title": "名称混淆：juspay/code-review-graph-rescript 同名变体"
        },
        {
          "id": "code-review-graph-3",
          "title": "安装后「我的配置被改了」/ 与 omc、agt 疑似冲突"
        },
        {
          "id": "code-review-graph-4",
          "title": "GitHub 直连慢/失败（依赖 GitHub 的场景）"
        },
        {
          "id": "pip-mirror-source",
          "title": "pip 安装超时/下载慢（ReadTimeoutError / WARNING: Retrying ... Read timed out）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-ai-code-review",
          "title": "AI 代码审查工作流（repomix + code-review-graph + swe-rex）",
          "page": "https://trove.xin/combos/combo-ai-code-review"
        },
        {
          "id": "combo-cicd-quality",
          "title": "CI/CD 质量门禁（swe-rex + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-cicd-quality"
        },
        {
          "id": "combo-governance-stack",
          "title": "开发链路治理栈（agent-governance-toolkit + code-review-graph + herdr）",
          "page": "https://trove.xin/combos/combo-governance-stack"
        },
        {
          "id": "combo-memory-session",
          "title": "单会话记忆治理栈（claude-mem + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-memory-session"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/code-review-graph",
        "human_en": "https://trove.xin/en/solutions/code-review-graph",
        "agent": "https://trove.xin/agent/solutions/code-review-graph",
        "detailed": "https://trove.xin/detailed/code-review-graph"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version",
          "expect": "预期输出: code-review-graph 2.3.8"
        },
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version",
          "expect": "预期输出: code-review-graph 2.3.8"
        },
        {
          "lang": "agent",
          "code": "code-review-graph build",
          "expect": "预期输出: 解析进度 + 统计（实测样例：Full build: 5 files, 9 nodes, 16 edges；工作区出现 .code-review-graph/graph.db）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph status",
          "expect": "预期输出: 节点/边/文件统计（实测样例：Nodes: 9 / Edges: 16 / Files: 5 / Languages: javascript, python / Built at commit: …）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph watch",
          "expect": "预期输出: Watching … for changes… (Ctrl+C to stop)（实测：改文件后 ~2s 自动增量，节点/边计数上升）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph install",
          "expect": "预期输出: 向已检测平台注册 MCP server（写 ~/.claude.json、~/.codex/hooks.json 等）"
        },
        {
          "lang": "agent",
          "code": "ls -d .code-review-graph 2>/dev/null && echo \"graph dir: OK\" || echo \"graph dir: MISSING\"\ncode-review-graph status",
          "expect": "预期输出: graph dir: OK 以及节点/边/文件统计"
        },
        {
          "lang": "agent",
          "code": "grep -l \"code-review-graph\" ~/.claude.json ~/.codex/hooks.json ~/.cursor/mcp.json 2>/dev/null || echo \"check config files manually\"",
          "expect": "预期输出: 至少一个配置文件路径（有注册的平台）；都没有则说明未注册"
        },
        {
          "lang": "agent",
          "code": "code-review-graph build",
          "expect": "预期输出: 解析进度 + 统计（实测样例：Full build: 5 files, 9 nodes, 16 edges；工作区出现 .code-review-graph/graph.db）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph status",
          "expect": "预期输出: 节点/边/文件统计（实测样例：Nodes: 9 / Edges: 16 / Files: 5 / Languages: javascript, python / Built at commit: …）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph watch",
          "expect": "预期输出: Watching … for changes… (Ctrl+C to stop)（实测：改文件后 ~2s 自动增量，节点/边计数上升）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph detect-changes",
          "expect": "预期输出: JSON 影响面报告（受影响函数/文件 + risk_score 字段）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph install",
          "expect": "预期输出: 向已检测平台注册 MCP server（写 ~/.claude.json、~/.codex/hooks.json 等）"
        },
        {
          "lang": "agent",
          "code": "code-review-graph uninstall 2>/dev/null || true\npip uninstall -y code-review-graph\ncommand -v code-review-graph >/dev/null 2>&1 && echo \"残留: code-review-graph 仍存在\" || echo \"code-review-graph removed: OK\"",
          "expect": "预期输出: code-review-graph removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "2.3.8",
          "max": "2.3.8"
        }
      }
    },
    {
      "name": "deepcode",
      "slug": "deepcode",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "2.1.0",
        "max": "2.1.0"
      },
      "license": {},
      "install": {
        "cmd": "pip install deepcode-hku && deepcode --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip 安装（清华镜像 --index-url，2026-08-27 本环境 venv 验证，deepcode 2.1.0，40.3s 全量依赖）；PyPI 官方当次 16s 边缘易超时，国内建议 --index-url https://pypi.tuna.tsinghua.edu.cn/simple（见指南安装段地区分支）；Python 3.12+；agent 跑通需先 deepcode init + deepcode provider 配置 LLM key"
      },
      "writes": [],
      "runtime": [],
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "summary": "开源 Agentic 编码框架：多智能体并行生成与修复代码、持久会话与目标跟踪，适合想本地跑完整编码 Agent 的开发者",
      "capabilities": [
        "多 Agent 编码",
        "持久会话/目标",
        "MCP 服务",
        "Skills/插件",
        "论文复现"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "claude-json-multi-tool",
          "title": "多个工具都写 ~/.claude.json，一起装会冲突吗？（实测兼容）"
        },
        {
          "id": "deepcode-2",
          "title": "git clone 连接被重置（源码安装路径）"
        },
        {
          "id": "deepcode-3",
          "title": "curl 证书报错（--ssl-no-revoke）"
        },
        {
          "id": "deepcode-4",
          "title": "旧 Python 装不上（requires-python）"
        },
        {
          "id": "deepcode-5",
          "title": "MCP client 起不来（Brave MCP WinError 2 / npx 未跑起来）"
        },
        {
          "id": "deepcode-6",
          "title": "装完跑 agent 无响应（需 init + provider 配置）"
        },
        {
          "id": "pip-mirror-source",
          "title": "pip 安装超时/下载慢（ReadTimeoutError / WARNING: Retrying ... Read timed out）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-auto-test",
          "title": "自动化测试闭环（静态分析 → 沙箱执行 → 浏览器 E2E）",
          "page": "https://trove.xin/combos/combo-auto-test"
        },
        {
          "id": "combo-cicd-quality",
          "title": "CI/CD 质量门禁（swe-rex + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-cicd-quality"
        },
        {
          "id": "combo-memory-session",
          "title": "单会话记忆治理栈（claude-mem + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-memory-session"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/deepcode",
        "human_en": "https://trove.xin/en/solutions/deepcode",
        "agent": "https://trove.xin/agent/solutions/deepcode",
        "detailed": "https://trove.xin/detailed/deepcode"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install deepcode-hku --index-url https://pypi.tuna.tsinghua.edu.cn/simple\ndeepcode --version",
          "expect": "预期输出: DeepCode 2.1.0"
        },
        {
          "lang": "agent",
          "code": "pip install deepcode-hku\ndeepcode --version",
          "expect": "预期输出: DeepCode 2.1.0"
        },
        {
          "lang": "agent",
          "code": "pip install deepcode-hku --index-url https://pypi.tuna.tsinghua.edu.cn/simple\ndeepcode --version",
          "expect": "预期输出: DeepCode 2.1.0"
        },
        {
          "lang": "agent",
          "code": "deepcode init",
          "expect": "预期输出: Initialized <home>/.deepcode/deepcode_config.json（已存在时提示使用 --force）"
        },
        {
          "lang": "agent",
          "code": "deepcode provider set openai --api-key <你的 key>",
          "expect": "预期输出: provider 配置成功提示（本地 provider 不需要云 API key）"
        },
        {
          "lang": "agent",
          "code": "deepcode exec \"写一个 python 的 hello world 并运行\" -w .",
          "expect": "预期输出: headless 任务运行过程与最终结果（多 Agent 编码系统执行）"
        },
        {
          "lang": "agent",
          "code": "deepcode --version\nls ~/.deepcode/deepcode_config.json 2>/dev/null && echo \"config: OK\" || echo \"config: MISSING\"",
          "expect": "预期输出: DeepCode 2.1.0；config: OK"
        },
        {
          "lang": "agent",
          "code": "deepcode exec \"写一个 python 的 hello world 并运行\" -w .",
          "expect": "预期输出: headless 任务运行过程与最终结果（本环境无 API key 未实测，命令来自 --help）"
        },
        {
          "lang": "agent",
          "code": "deepcode --help",
          "expect": "预期输出: 以 Usage: 开头，含 init/test/mcp/skill/plugin/provider/session/exec/loop/schedule/automation"
        },
        {
          "lang": "agent",
          "code": "deepcode init",
          "expect": "预期输出: Initialized <home>/.deepcode/deepcode_config.json（已存在时提示使用 --force）"
        },
        {
          "lang": "agent",
          "code": "deepcode provider set openai --api-key <你的 key>",
          "expect": "预期输出: provider 配置成功提示（本地 provider 不需要云 API key）"
        },
        {
          "lang": "agent",
          "code": "deepcode exec \"写一个 python 的 hello world 并运行\" -w .",
          "expect": "预期输出: headless 任务运行过程与最终结果（多 Agent 编码系统执行）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y deepcode-hku || true\ncommand -v deepcode >/dev/null 2>&1 && echo \"deepcode 仍存在\" || echo \"deepcode removed: OK\"",
          "expect": "预期输出: deepcode removed: OK"
        },
        {
          "lang": "agent",
          "code": "rm -rf ~/.deepcode",
          "expect": ""
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "2.1.0",
          "max": "2.1.0"
        }
      }
    },
    {
      "name": "frp",
      "slug": "frp",
      "category": "network",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.52.0",
        "max": "0.71.0"
      },
      "license": {},
      "install": {
        "cmd": "curl -L -o /tmp/frp.tar.gz https://ghfast.top/https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_amd64.tar.gz && tar xzf /tmp/frp.tar.gz -C /tmp && sudo mv /tmp/frp_0.71.0_linux_amd64/frps /tmp/frp_0.71.0_linux_amd64/frpc /usr/local/bin/",
        "interactive": false,
        "semi_auto_note": "实测 2026-08-31：v0.71.0（2026-08-14 发布，Apache-2.0，单二进制无依赖）；下载走 ghfast.top 加速镜像（直连 GitHub release 在部分网络不可达，Gitee 仓库镜像不带 release 资产）；隔离回环隧道全链路实测通过（实测复现日志（2026-08-31））。"
      },
      "writes": [],
      "runtime": [],
      "domain": "",
      "domain_label": "",
      "summary": "内网穿透/反向隧道：无公网 IP 也能经公网 frps 服务器访问内网服务（SSH/Web/远程联调）",
      "capabilities": [
        "TCP 端口映射（SSH/任意 TCP 服务）",
        "HTTP/HTTPS 虚拟主机（vhost 域名转发）",
        "token 认证 + 默认 TLS（v0.50.0 起）",
        "每代理加密/压缩开关",
        "frpc 热加载配置"
      ],
      "validated_at": "2026-08-31",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/frp",
        "human_en": "https://trove.xin/en/solutions/frp",
        "agent": "https://trove.xin/agent/solutions/frp",
        "detailed": "https://trove.xin/detailed/frp"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "mkdir -p /tmp/frp-demo && cd /tmp/frp-demo && curl -L -o frp.tar.gz https://ghfast.top/https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_amd64.tar.gz && tar xzf frp.tar.gz --strip-components=1 && printf 'frp tunnel demo page\\n' > index.html && (python3 -m http.server 27081 --bind 127.0.0.1 >/dev/null 2>&1 &) && printf 'bindAddr = \"127.0.0.1\"\\nbindPort = 27000\\n' > frps.toml && ./frps -c frps.toml >/dev/null 2>&1 & sleep 1; printf 'serverAddr = \"127.0.0.1\"\\nserverPort = 27000\\n\\n[[proxies]]\\nname = \"web-demo\"\\ntype = \"tcp\"\\nlocalIP = \"127.0.0.1\"\\nlocalPort = 27081\\nremotePort = 27082\\n' > frpc.toml && ./frpc -c frpc.toml >/dev/null 2>&1 & sleep 2; curl -s http://127.0.0.1:27082/index.html",
          "expect": "预期输出: `frp tunnel demo page`（隧道通）；否则按「一步步来」分段排查"
        },
        {
          "lang": "agent",
          "code": "curl -L -o /tmp/frp.tar.gz https://ghfast.top/https://github.com/fatedier/frp/releases/download/v0.71.0/frp_0.71.0_linux_amd64.tar.gz\ntar xzf /tmp/frp.tar.gz -C /tmp\nsudo mv /tmp/frp_0.71.0_linux_amd64/frps /tmp/frp_0.71.0_linux_amd64/frpc /usr/local/bin/\nfrps --version && frpc --version",
          "expect": "预期输出: `0.71.0`（两行）"
        },
        {
          "lang": "agent",
          "code": "sudo mkdir -p /etc/frp\nsudo tee /etc/frp/frps.toml > /dev/null <<'TOML'\nbindAddr = \"0.0.0.0\"\nbindPort = 7000\nauth.method = \"token\"\nauth.token = \"换成你自己的长随机串\"\nTOML\nsudo /usr/local/bin/frps -c /etc/frp/frps.toml",
          "expect": "预期输出: `frps uses config file: /etc/frp/frps.toml` `frps tcp listen on 0.0.0.0:7000` `frps started successfully`"
        },
        {
          "lang": "agent",
          "code": "sudo tee /etc/frp/frpc.toml > /dev/null <<'TOML'\nserverAddr = \"你的服务器公网 IP\"\nserverPort = 7000\nauth.method = \"token\"\nauth.token = \"和 frps 一致的 token\"\n\n[[proxies]]\nname = \"ssh-home\"\ntype = \"tcp\"\nlocalIP = \"127.0.0.1\"\nlocalPort = 22\nremotePort = 6000\nTOML\nsudo /usr/local/bin/frpc -c /etc/frp/frpc.toml",
          "expect": "预期输出: `login to server success` `proxy added: [ssh-home]` `[ssh-home] start proxy success`"
        },
        {
          "lang": "agent",
          "code": "sudo tee /etc/systemd/system/frpc.service > /dev/null <<'UNIT'\n[Unit]\nDescription=frp client\nAfter=network.target\n\n[Service]\nExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\nUNIT\nsudo systemctl enable --now frpc\nsudo systemctl status frpc --no-pager",
          "expect": "预期输出: systemd 状态 Active: active (running)（2026-09-01 实测 transient unit：active + 隧道 200 + stop 后隧道关闭，证据 实测复现日志（2026-09-01））"
        },
        {
          "lang": "agent",
          "code": "curl -s http://你的服务器公网IP:映射出的remotePort/ -o /dev/null -w '%{http_code}\\n'\nssh -oPort=6000 你的用户名@你的服务器公网IP 'echo tunnel-ok'\nfrpc status -c /etc/frp/frpc.toml",
          "expect": "预期输出: HTTP `200`；SSH 回显 `tunnel-ok`；frpc status 列出代理 `web-demo/ssh-home` 状态为 running（本地隔离演示的等价验证见「验证证据」，`curl 127.0.0.1:27082` 返回与直连一致）"
        },
        {
          "lang": "agent",
          "code": "cat >> /etc/frp/frpc.toml <<'TOML'\nwebServer.addr = \"127.0.0.1\"\nwebServer.port = 7400\nTOML",
          "expect": "预期输出: （无输出；重跑 status/reload 前需要重启一次 frpc 让 admin server 生效）"
        },
        {
          "lang": "agent",
          "code": "frpc status -c /etc/frp/frpc.toml",
          "expect": "预期输出: `Proxy Status...` 表格，每个代理一行（实测 v0.71.0）：`Name  Status  LocalAddr  Plugin  RemoteAddr  Error`，行如 `web-demo  running  127.0.0.1:27081  127.0.0.1:27082`（Status 为 `running` 即正常）"
        },
        {
          "lang": "agent",
          "code": "frpc reload -c /etc/frp/frpc.toml",
          "expect": "预期输出: `reload success`（实测：2 个代理运行中 reload 新增第 3 条，新代理立即 running，旧代理不受影响）"
        },
        {
          "lang": "agent",
          "code": "sudo systemctl disable --now frpc 2>/dev/null || true\nsudo rm -f /usr/local/bin/frps /usr/local/bin/frpc\nsudo rm -rf /etc/frp\npkill -f frpc; pkill -f frps 2>/dev/null",
          "expect": "预期输出: （无输出；/usr/local/bin 与 /etc/frp 已清理）"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-31",
        "ttl_due": "2027-02-27",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.52.0",
          "max": "0.71.0"
        }
      }
    },
    {
      "name": "herdr",
      "slug": "herdr",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.8.2",
        "max": "0.8.2"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64 && chmod +x herdr && ./herdr --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = release 二进制下载（2026-08-28 本环境验证，v0.8.2，gh-proxy 加速；GitHub 直连慢）；Rust 单二进制，无运行时依赖；启动完整客户端会拉起后台 server（~/.config/herdr/ 下 socket + 日志 + session.json，~/.local/state/herdr/agent-detection/ 下 agent 清单）；tmux 类终端面（session/workspace/worktree/tab/pane）；有 herdr server stop 停止面"
      },
      "writes": [],
      "runtime": [],
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "summary": "编码 agent 的终端运行时：持久会话、工作区/工作树管理、agent 面板与多工具集成",
      "capabilities": [
        "持久会话（tmux 面）",
        "workspace/worktree 管理",
        "agent/终端面板",
        "远程主机会话",
        "agent 检测清单",
        "本地 server + socket API"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "herdr-1",
          "title": "`herdr server start` 无效 / 后台 server 未拉起（v0.8.2 没有 server start 子命令）"
        },
        {
          "id": "herdr-2",
          "title": "Windows 上 herdr 用 PowerShell 安装失败（下载被重置；改直下二进制）"
        },
        {
          "id": "herdr-3",
          "title": "SSH 远程用 herdr 中文显示乱码/输入无效（终端编码与 locale）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        },
        {
          "id": "combo-governance-stack",
          "title": "开发链路治理栈（agent-governance-toolkit + code-review-graph + herdr）",
          "page": "https://trove.xin/combos/combo-governance-stack"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/herdr",
        "human_en": "https://trove.xin/en/solutions/herdr",
        "agent": "https://trove.xin/agent/solutions/herdr",
        "detailed": "https://trove.xin/detailed/herdr"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64\nchmod +x herdr\n./herdr --version",
          "expect": "预期输出: herdr 0.8.2"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64\nchmod +x herdr\n./herdr --version",
          "expect": "预期输出: herdr 0.8.2"
        },
        {
          "lang": "agent",
          "code": "herdr status",
          "expect": "预期输出: client 版本 0.8.2 + channel stable + server 状态（not running 时给出 socket 路径）"
        },
        {
          "lang": "agent",
          "code": "herdr config check",
          "expect": "预期输出: config: ok（无 config.toml 时也通过——首次运行以默认配置为准）"
        },
        {
          "lang": "agent",
          "code": "herdr --session main",
          "expect": "预期输出: 进入持久会话终端面（首次运行创建 ~/.config/herdr/ + ~/.local/state/herdr/agent-detection/；退出会话 server 可能常驻）"
        },
        {
          "lang": "agent",
          "code": "herdr status\nherdr config check",
          "expect": "预期输出: 版本 0.8.2 + channel stable + server 状态；config: ok"
        },
        {
          "lang": "agent",
          "code": "herdr --session main",
          "expect": "预期输出: 进入持久会话终端面（首次运行创建 ~/.config/herdr/ + ~/.local/state/herdr/）"
        },
        {
          "lang": "agent",
          "code": "herdr --help",
          "expect": "预期输出: session/workspace/worktree/tab/notification/agent/pane/integration/server/config/channel/api 等子命令"
        },
        {
          "lang": "agent",
          "code": "herdr status",
          "expect": "预期输出: client 版本 0.8.2 + channel stable + server 状态（not running 时给出 socket 路径）"
        },
        {
          "lang": "agent",
          "code": "herdr config check",
          "expect": "预期输出: config: ok（无 config.toml 时也通过——首次运行以默认配置为准）"
        },
        {
          "lang": "agent",
          "code": "herdr --session main",
          "expect": "预期输出: 进入持久会话终端面（首次运行创建 ~/.config/herdr/ + ~/.local/state/herdr/agent-detection/；退出会话 server 可能常驻）"
        },
        {
          "lang": "agent",
          "code": "herdr server stop",
          "expect": "预期输出: server stopped（通过 socket API）"
        },
        {
          "lang": "agent",
          "code": "herdr server stop 2>/dev/null || true\nrm -f /usr/local/bin/herdr ~/.local/bin/herdr\nrm -rf ~/.config/herdr ~/.local/state/herdr\ncommand -v herdr >/dev/null 2>&1 && echo '残留: herdr 仍存在' || echo 'herdr removed: OK'",
          "expect": "预期输出: herdr removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.8.2",
          "max": "0.8.2"
        }
      }
    },
    {
      "name": "hoop",
      "slug": "hoop",
      "category": "gate",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.3.2",
        "max": "0.3.2"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o hsh.tar.gz https://gh-proxy.com/https://github.com/hoophq/hsh/releases/download/v0.3.2/hsh-linux-x64.tar.gz && tar xzf hsh.tar.gz && ./hsh-linux-x64/hsh --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = hsh CLI tarball（2026-08-28 本环境验证，hsh 0.3.2，gh-proxy 加速）；完整形态 = hsh CLI（Bun 二进制，非特权）+ hsh-tunneld 守护进程（Go，需 systemd + sudo，install.sh 安装到 /etc/hsh/ + /var/run/hsh/，本环境未执行特权安装）；hoop 仓库 release 1.150.1（2026-08-27）提供 hsh-tunneld + install/uninstall 脚本；hsh CLI 仓库 hoophq/hsh v0.3.2（2026-06-03）提供全平台 tarball"
      },
      "writes": [],
      "runtime": [],
      "domain": "gateway",
      "domain_label": "网关与路由",
      "summary": "统一协议网关（MCP/LLM/数据库同一策略面；hsh 网关 CLI + 特权隧道守护进程）",
      "capabilities": [
        "统一网关（MCP/LLM/DB）",
        "shell 插件（ssh/kubectl）",
        "临时 kubeconfig 签发",
        "本地隧道（*.hoop 域名）",
        "shell 集成（shell-init）"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        }
      ],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/hoop",
        "human_en": "https://trove.xin/en/solutions/hoop",
        "agent": "https://trove.xin/agent/solutions/hoop",
        "detailed": "https://trove.xin/detailed/hoop"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o hsh.tar.gz https://gh-proxy.com/https://github.com/hoophq/hsh/releases/download/v0.3.2/hsh-linux-x64.tar.gz\ntar xzf hsh.tar.gz\n./hsh-linux-x64/hsh --version",
          "expect": "预期输出: 0.3.2 (hsh-tunneld 1.86.0)"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o hsh.tar.gz https://gh-proxy.com/https://github.com/hoophq/hsh/releases/download/v0.3.2/hsh-linux-x64.tar.gz\ntar xzf hsh.tar.gz\n./hsh-linux-x64/hsh --version",
          "expect": "预期输出: 0.3.2 (hsh-tunneld 1.86.0)"
        },
        {
          "lang": "agent",
          "code": "hsh config set api-url https://your-hoop-gateway",
          "expect": "预期输出: 配置写入 ~/.hsh/"
        },
        {
          "lang": "agent",
          "code": "hsh status",
          "expect": "预期输出: Version 0.3.2 + API URL（未配置）+ Auth（未认证）+ Plugins（ssh, kubectl）"
        },
        {
          "lang": "agent",
          "code": "./hsh-linux-x64/hsh --version",
          "expect": "预期输出: 0.3.2 (hsh-tunneld 1.86.0)"
        },
        {
          "lang": "agent",
          "code": "hsh status",
          "expect": "预期输出: Version 0.3.2 + API URL（已配置）+ Auth（未认证）+ Plugins（ssh, kubectl）"
        },
        {
          "lang": "agent",
          "code": "hsh status",
          "expect": "预期输出: Version 0.3.2 + API URL（未配置）+ Auth（未认证）+ Plugins（ssh, kubectl）"
        },
        {
          "lang": "agent",
          "code": "hsh config set api-url https://your-hoop-gateway",
          "expect": "预期输出: 配置写入 ~/.hsh/"
        },
        {
          "lang": "agent",
          "code": "hsh login",
          "expect": "预期输出: 认证流程（token 存 ~/.hsh/）"
        },
        {
          "lang": "agent",
          "code": "hsh kubeconfig prod-db",
          "expect": "预期输出: 临时 kubeconfig 路径（供 k9s/helm/kubectl 使用）"
        },
        {
          "lang": "agent",
          "code": "hsh tunnel status",
          "expect": "预期输出: hsh-tunneld 守护进程状态（未安装时提示）"
        },
        {
          "lang": "agent",
          "code": "rm -rf ~/.hsh\nrm -f /usr/local/bin/hsh /usr/local/bin/hsh-tunneld\nsudo systemctl stop hsh-tunneld 2>/dev/null || true\ncommand -v hsh >/dev/null 2>&1 && echo '残留: hsh 仍存在' || echo 'hoop removed: OK'",
          "expect": "预期输出: hoop removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.3.2",
          "max": "0.3.2"
        }
      }
    },
    {
      "name": "litellm",
      "slug": "litellm",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.98.0",
        "max": "1.98.0"
      },
      "license": {},
      "install": {
        "cmd": "python3 -m venv ~/.litellm-venv && ~/.litellm-venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple litellm[proxy] && ~/.litellm-venv/bin/litellm --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = 清华镜像 pip install litellm[proxy]（2026-08-29 本环境验证，隔离 venv，Python 3.14，安装约 80s）；服务端网关需自备模型 API key（零账号实测用 openai/mock + dummy key 冒烟：proxy 启动 + /health/liveliness + /v1/models 全过）；proxy 默认端口 4000（--port 可改）"
      },
      "writes": [],
      "runtime": [],
      "domain": "gateway",
      "domain_label": "网关与路由",
      "summary": "统一 LLM 网关：一个 OpenAI 兼容端点接 100+ 模型，统一 key 管理/成本追踪/负载均衡",
      "capabilities": [
        "OpenAI 兼容端点（/v1/chat/completions）",
        "100+ 供应商统一接入",
        "key 管理与成本追踪",
        "负载均衡与限流",
        "proxy 模式（服务端网关）"
      ],
      "validated_at": "2026-08-29",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "logfire",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-08-29)"
        }
      ],
      "related_problems": [],
      "related_combos": [
        {
          "id": "combo-model-access",
          "title": "多模型接入与成本可观测",
          "page": "https://trove.xin/combos/combo-model-access"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/litellm",
        "human_en": "https://trove.xin/en/solutions/litellm",
        "agent": "https://trove.xin/agent/solutions/litellm",
        "detailed": "https://trove.xin/detailed/litellm"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "python3 -m venv ~/.litellm-venv\n~/.litellm-venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple litellm[proxy]\n~/.litellm-venv/bin/litellm --version",
          "expect": "预期输出: LiteLLM: Current Version = 1.98.0"
        },
        {
          "lang": "agent",
          "code": "python3 -m venv ~/.litellm-venv\n~/.litellm-venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple litellm[proxy]\n~/.litellm-venv/bin/litellm --version",
          "expect": "预期输出: LiteLLM: Current Version = 1.98.0"
        },
        {
          "lang": "agent",
          "code": "cat > /tmp/ltm-config.yaml <<'EOF'\nmodel_list:\n  - model_name: mock-gpt\n    litellm_params:\n      model: openai/mock\n      api_key: dummy\nlitellm_settings:\n  drop_params: true\nEOF",
          "expect": "预期输出: 配置文件就绪（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "~/.litellm-venv/bin/litellm --config /tmp/ltm-config.yaml --port 8099",
          "expect": "预期输出: Uvicorn running on http://0.0.0.0:8099（Application startup complete；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:8099/health/liveliness\ncurl -s http://127.0.0.1:8099/v1/models",
          "expect": "预期输出: \"I'm alive!\"；{\"data\":[{\"id\":\"mock-gpt\",...}]}（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "~/.litellm-venv/bin/litellm --version",
          "expect": "预期输出: LiteLLM: Current Version = 1.98.0"
        },
        {
          "lang": "agent",
          "code": "~/.litellm-venv/bin/litellm --config /tmp/ltm-config.yaml --port 8099",
          "expect": "预期输出: Uvicorn running on http://0.0.0.0:8099（Application startup complete）"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:8099/health/liveliness\ncurl -s http://127.0.0.1:8099/v1/models",
          "expect": "预期输出: \"I'm alive!\"；{\"data\":[{\"id\":\"mock-gpt\",...}]}"
        },
        {
          "lang": "agent",
          "code": "cat > /tmp/ltm-config.yaml <<'EOF'\nmodel_list:\n  - model_name: mock-gpt\n    litellm_params:\n      model: openai/mock\n      api_key: dummy\nlitellm_settings:\n  drop_params: true\nEOF",
          "expect": "预期输出: 配置文件就绪（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "~/.litellm-venv/bin/litellm --config /tmp/ltm-config.yaml --port 8099",
          "expect": "预期输出: Uvicorn running on http://0.0.0.0:8099（Application startup complete；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:8099/health/liveliness\ncurl -s http://127.0.0.1:8099/v1/models",
          "expect": "预期输出: \"I'm alive!\"；{\"data\":[{\"id\":\"mock-gpt\",...}]}（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "pkill -f 'litellm.*--config' 2>/dev/null || true\nrm -rf ~/.litellm-venv",
          "expect": "预期输出: 无残留（venv 删除后命令面消失）"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.98.0",
          "max": "1.98.0"
        }
      }
    },
    {
      "name": "logfire",
      "slug": "logfire",
      "category": "metrics",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "4.41.0",
        "max": "4.41.0"
      },
      "license": {},
      "install": {
        "cmd": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple logfire==4.41.0 && logfire --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip install -i 清华镜像 logfire==4.41.0（2026-08-28 本环境验证，隔离 venv，Python 3.14）；国内必须带清华镜像（PyPI 官方超时）；依赖 opentelemetry 全家桶，首次安装约 3-5 分钟；上传托管平台需 logfire auth 登录（token 存 ~/.logfire/），本地 preview 模式可无 token；无 daemon 常驻"
      },
      "writes": [],
      "runtime": [],
      "domain": "observability",
      "domain_label": "可观测性",
      "summary": "AI/LLM 与 Python 应用可观测平台（OpenTelemetry 原生）：日志、追踪、指标一站式，适合排障与性能分析",
      "capabilities": [
        "LLM 调用追踪",
        "成本/用量度量",
        "OpenTelemetry 集成",
        "本地 preview 模式",
        "Python 自动插桩"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "superpowers",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "code-review-graph",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "logfire-1",
          "title": "FastAPI+SQLModel 测试报 sqlalchemy.exc.NoInspectionAvailable"
        },
        {
          "id": "logfire-2",
          "title": "升级后刷 Invalid type NoneType for attribute 'http.port' value 警告"
        },
        {
          "id": "pip-mirror-source",
          "title": "pip 安装超时/下载慢（ReadTimeoutError / WARNING: Retrying ... Read timed out）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-model-access",
          "title": "多模型接入与成本可观测",
          "page": "https://trove.xin/combos/combo-model-access"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/logfire",
        "human_en": "https://trove.xin/en/solutions/logfire",
        "agent": "https://trove.xin/agent/solutions/logfire",
        "detailed": "https://trove.xin/detailed/logfire"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple logfire==4.41.0\nlogfire --version",
          "expect": "预期输出: Running Logfire 4.41.0 with CPython ..."
        },
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple logfire==4.41.0\nlogfire --version",
          "expect": "预期输出: Running Logfire 4.41.0 with CPython ..."
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import logfire; logfire.configure; print('logfire configured: OK')\"",
          "expect": "预期输出: logfire configured: OK（本地 preview 无 token 可跑）"
        },
        {
          "lang": "agent",
          "code": "logfire auth",
          "expect": "预期输出: 浏览器/设备码登录流程（交互式；token 存 ~/.logfire/）"
        },
        {
          "lang": "agent",
          "code": "logfire preview",
          "expect": "预期输出: 本地预览地址（浏览 trace/span；Ctrl+C 退出）"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import logfire; logfire.configure; print('logfire configured: OK')\"",
          "expect": "预期输出: logfire configured: OK（本地 preview 无 token 可跑）"
        },
        {
          "lang": "agent",
          "code": "logfire --version",
          "expect": "预期输出: Running Logfire 4.41.0 with CPython ..."
        },
        {
          "lang": "agent",
          "code": "logfire --help",
          "expect": "预期输出: CLI 说明 + 全局选项（--non-interactive/--base-url/--region）"
        },
        {
          "lang": "agent",
          "code": "logfire auth",
          "expect": "预期输出: 浏览器/设备码登录流程（交互式；token 存 ~/.logfire/）"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import logfire; logfire.configure; print('logfire configured: OK')\"",
          "expect": "预期输出: logfire configured: OK（本地 preview 无 token 可跑）"
        },
        {
          "lang": "agent",
          "code": "logfire preview",
          "expect": "预期输出: 本地预览地址（浏览 trace/span；Ctrl+C 退出）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y logfire logfire-api 2>/dev/null || true\nrm -rf ~/.logfire\ncommand -v logfire >/dev/null 2>&1 && echo \"残留: logfire 仍存在\" || echo \"logfire removed: OK\"",
          "expect": "预期输出: logfire removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "4.41.0",
          "max": "4.41.0"
        }
      }
    },
    {
      "name": "moltis",
      "slug": "moltis",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "20260830.01",
        "max": "20260830.01"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o /tmp/moltis.tar.gz 'https://gh-proxy.com/https://github.com/moltis-org/moltis/releases/download/20260827.01/moltis-20260827.01-x86_64-unknown-linux-gnu.tar.gz' && tar xzf /tmp/moltis.tar.gz -C /usr/local/bin moltis && moltis --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = release 二进制（2026-08-28 本环境验证，20260827.01）；国内直连 GitHub release 资产实测 3KB/s 超时——安装必须走 gh-proxy 类公共加速（本条命令已内置）或镜像；其他平台选对应资产（AppImage/dmg/exe/deb）；首次运行自动生成 ~/.config/moltis/；gateway 为默认子命令（端口 + 常驻进程），doctor 检查配置；完整服务需 onboard 配置 provider"
      },
      "writes": [],
      "runtime": [],
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "summary": "安全的持久个人 Agent 服务器（Rust 单二进制）：一个常驻服务接网页/Telegram/WhatsApp 通道，适合自托管个人 AI 助手",
      "capabilities": [
        "沙箱化执行",
        "多 provider LLM",
        "记忆/会话持久化",
        "消息通道（Telegram/WhatsApp 等）",
        "onboard/doctor 配置向导"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "superpowers",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "code-review-graph",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "logfire",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        }
      ],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/moltis",
        "human_en": "https://trove.xin/en/solutions/moltis",
        "agent": "https://trove.xin/agent/solutions/moltis",
        "detailed": "https://trove.xin/detailed/moltis"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/moltis.tar.gz 'https://gh-proxy.com/https://github.com/moltis-org/moltis/releases/download/20260830.01/moltis-20260830.01-x86_64-unknown-linux-gnu.tar.gz'\ntar xzf /tmp/moltis.tar.gz -C /usr/local/bin moltis\nmoltis --version",
          "expect": "预期输出: moltis 20260830.01"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/moltis.tar.gz 'https://gh-proxy.com/https://github.com/moltis-org/moltis/releases/download/20260830.01/moltis-20260830.01-x86_64-unknown-linux-gnu.tar.gz'\ntar xzf /tmp/moltis.tar.gz -C /usr/local/bin moltis\nmoltis --version",
          "expect": "预期输出: moltis 20260830.01"
        },
        {
          "lang": "agent",
          "code": "moltis doctor",
          "expect": "预期输出: 配置/安全检查项（TOML 有效、无 API key 泄露；数据目录权限 warn 可手动 chmod 700）"
        },
        {
          "lang": "agent",
          "code": "moltis onboard",
          "expect": "预期输出: 交互式配置（provider/通道/沙箱；人工介入）"
        },
        {
          "lang": "agent",
          "code": "moltis gateway",
          "expect": "预期输出: 网关启动日志（Ctrl+C 停止；生产用 systemd 托管）"
        },
        {
          "lang": "agent",
          "code": "moltis --version\nmoltis doctor",
          "expect": "预期输出: 20260830.01；配置/安全检查项（TOML 有效、无 API key 泄露；数据目录权限 warn 可手动 chmod 700）"
        },
        {
          "lang": "agent",
          "code": "moltis gateway",
          "expect": "预期输出: 网关启动日志（Ctrl+C 停止；生产用 systemd 托管）"
        },
        {
          "lang": "agent",
          "code": "moltis --help",
          "expect": "预期输出: gateway/acp/agent/channels/sessions/config/models/providers/onboard/doctor/auth/skills/hooks/sandbox 等命令"
        },
        {
          "lang": "agent",
          "code": "moltis doctor",
          "expect": "预期输出: 配置/安全检查项（TOML 有效、无 API key 泄露；数据目录权限 warn 可手动 chmod 700）"
        },
        {
          "lang": "agent",
          "code": "moltis onboard",
          "expect": "预期输出: 交互式配置（provider/通道/沙箱；人工介入）"
        },
        {
          "lang": "agent",
          "code": "moltis gateway",
          "expect": "预期输出: 网关启动日志（Ctrl+C 停止；生产用 systemd 托管）"
        },
        {
          "lang": "agent",
          "code": "rm -f /usr/local/bin/moltis\nrm -rf ~/.config/moltis\ncommand -v moltis >/dev/null 2>&1 && echo \"残留: moltis 仍存在\" || echo \"moltis removed: OK\"",
          "expect": "预期输出: moltis removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "20260830.01",
          "max": "20260830.01"
        }
      }
    },
    {
      "name": "oh-my-pi",
      "slug": "oh-my-pi",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "18.0.11",
        "max": "18.0.11"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o omp https://gh-proxy.com/https://github.com/can1357/oh-my-pi/releases/download/v18.0.11/omp-linux-x64 && chmod +x omp && ./omp --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = release 二进制下载（2026-08-28 本环境验证，v18.0.11，gh-proxy 加速；GitHub 直连慢）；单二进制 183MB；运行即写 ~/.omp/logs/（日志 + audit.json，实测）；会话存储 ~/.omp/agent（PI_CODING_AGENT_DIR 可重定位）；完整编码会话需 API key（ANTHROPIC/OPENAI 等，未实测——无 key）；IDE 形态 = VS Code 扩展（官方，未实测）"
      },
      "writes": [],
      "runtime": [],
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "summary": "接入 IDE 的编码 agent（CLI + VS Code 扩展；Claude Code/Codex 类工作流）",
      "capabilities": [
        "编码 agent CLI",
        "IDE 联动（VS Code 扩展）",
        "多模型路由（smol/slow/plan）",
        "子代理并行（task）",
        "浏览器/计算机工具",
        "Profile 隔离状态"
      ],
      "validated_at": "2026-08-31",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "oh-my-pi-1",
          "title": "安装成功但 omp --version 报 SyntaxError（旧版 bun）"
        },
        {
          "id": "oh-my-pi-2",
          "title": "命令混淆：omp install 与 omp plugin install"
        },
        {
          "id": "oh-my-pi-3",
          "title": "Windows/ARM release 二进制加载外部扩展失败"
        },
        {
          "id": "oh-my-pi-4",
          "title": "名称混淆：oh-my-pi（omp）vs pi-mono（pi）"
        }
      ],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/oh-my-pi",
        "human_en": "https://trove.xin/en/solutions/oh-my-pi",
        "agent": "https://trove.xin/agent/solutions/oh-my-pi",
        "detailed": "https://trove.xin/detailed/oh-my-pi"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o omp https://gh-proxy.com/https://github.com/can1357/oh-my-pi/releases/download/v18.0.11/omp-linux-x64\nchmod +x omp\n./omp --version",
          "expect": "预期输出: omp/18.0.11"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o omp https://gh-proxy.com/https://github.com/can1357/oh-my-pi/releases/download/v18.0.11/omp-linux-x64\nchmod +x omp\n./omp --version",
          "expect": "预期输出: omp/18.0.11"
        },
        {
          "lang": "agent",
          "code": "omp --print \"explain this repo in one sentence\" --model gpt-5.2",
          "expect": "预期输出: 模型回复（文本模式）"
        },
        {
          "lang": "agent",
          "code": "omp agents unpack",
          "expect": "预期输出: 导出内置子代理到 ~/.omp/agent/agents（项目形态用 --project → ./.omp/agents）"
        },
        {
          "lang": "agent",
          "code": "omp --profile work --alias omp-work",
          "expect": "预期输出: 创建 shell 别名 omp-work（隔离 auth/sessions/settings/caches）"
        },
        {
          "lang": "agent",
          "code": "./omp --version\n./omp --help",
          "expect": "预期输出: omp/18.0.11；完整 flags 与工具清单"
        },
        {
          "lang": "agent",
          "code": "omp --print \"explain this repo in one sentence\" --model gpt-5.2",
          "expect": "预期输出: 模型回复（文本模式；本环境无 API key 未实测）"
        },
        {
          "lang": "agent",
          "code": "omp --help",
          "expect": "预期输出: USAGE + FLAGS（--model/--provider/--profile/--mode/--print 等）+ 环境变量面（OMP_PROFILE/PI_CODING_AGENT_DIR）+ 工具清单（read/bash/edit/write/task/browser 等）"
        },
        {
          "lang": "agent",
          "code": "omp --print \"explain this repo in one sentence\" --model gpt-5.2",
          "expect": "预期输出: 模型回复（文本模式）"
        },
        {
          "lang": "agent",
          "code": "omp agents unpack",
          "expect": "预期输出: 导出内置子代理到 ~/.omp/agent/agents（项目形态用 --project → ./.omp/agents）"
        },
        {
          "lang": "agent",
          "code": "omp --profile work --alias omp-work",
          "expect": "预期输出: 创建 shell 别名 omp-work（隔离 auth/sessions/settings/caches）"
        },
        {
          "lang": "agent",
          "code": "rm -f /usr/local/bin/omp ~/.local/bin/omp\nrm -rf ~/.omp\ncommand -v omp >/dev/null 2>&1 && echo '残留: omp 仍存在' || echo 'oh-my-pi removed: OK'",
          "expect": "预期输出: oh-my-pi removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-31",
        "ttl_due": "2027-02-27",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "18.0.11",
          "max": "18.0.11"
        }
      }
    },
    {
      "name": "ollama",
      "slug": "ollama",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.33.2",
        "max": "0.33.2"
      },
      "license": {},
      "install": {
        "cmd": "curl -fsSL https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst -o /tmp/ollama.tar.zst && tar --zstd -xf /tmp/ollama.tar.zst -C /usr/local && ollama --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = gh-proxy 拉取 ollama-linux-amd64.tar.zst（1.42GB，v0.33.2）→ tar --zstd 解压（zstd 1.5.7，apt 安装）→ 冒烟全过（2026-08-29，证据 实测复现日志（2026-08-29））；官方 install.sh 依赖 ollama.com 直连（国内可能慢，推断）；模型下载默认 ollama 官方 registry，国内可能超时（实测观察）"
      },
      "writes": [],
      "runtime": [],
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "summary": "本地大模型运行底座：一行命令拉起 Qwen/DeepSeek/Llama 等模型服务，OpenAI 兼容 API，改 base_url 接入任意客户端",
      "capabilities": [
        "模型下载与运行（本地推理）",
        "OpenAI 兼容 API（/api/version 等）",
        "OLLAMA_MODELS 模型目录可配置",
        "默认端口 11434",
        "无 GPU 自动回退 CPU 推理"
      ],
      "validated_at": "2026-08-29",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "open-webui",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "llama-factory",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "function-calling-guard",
          "title": "LLM Function Calling 不可控？AI 建议的危险调用必须过白名单（本地 qwen 实测）"
        },
        {
          "id": "ollama-connect-failed",
          "title": "ollama 命令报 \"could not connect to a running Ollama instance\"（服务没启动）"
        },
        {
          "id": "ollama-install-slow",
          "title": "ollama 装不上/官方安装脚本国内拉不动（下载慢、超时）"
        },
        {
          "id": "ollama-models-migration",
          "title": "模型都下到 C 盘/系统盘了，怎么把 Ollama 模型目录换到别的盘"
        },
        {
          "id": "ollama-pull-timeout",
          "title": "ollama pull 卡住/超时（官方 registry 国内直连不稳定）"
        },
        {
          "id": "open-webui-rag-embedding",
          "title": "Open WebUI 知识库检索不生效 / embedding 配置错（上传文件报 Ollama embed 404）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-local-llm-finetune",
          "title": "本地大模型微调/推理环境（ollama + open-webui + LLaMA-Factory）",
          "page": "https://trove.xin/combos/combo-local-llm-finetune"
        },
        {
          "id": "combo-local-rag",
          "title": "本地知识库问答（ollama + MaxKB）",
          "page": "https://trove.xin/combos/combo-local-rag"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/ollama",
        "human_en": "https://trove.xin/en/solutions/ollama",
        "agent": "https://trove.xin/agent/solutions/ollama",
        "detailed": "https://trove.xin/detailed/ollama"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -fsSL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst\ntar --zstd -xf /tmp/ollama.tar.zst -C /usr/local\nollama --version",
          "expect": "预期输出: ollama version 0.33.2（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -fsSL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst\ntar --zstd -xf /tmp/ollama.tar.zst -C /usr/local\nollama --version",
          "expect": "预期输出: ollama version 0.33.2（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "OLLAMA_MODELS=~/.ollama/models ollama serve",
          "expect": "预期输出: server 配置日志正常（ssh key 生成、blobs: 0；默认端口 11434；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:11434/api/version",
          "expect": "预期输出: {\"version\":\"0.33.2\"}（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "ollama --version",
          "expect": "预期输出: ollama version 0.33.2"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:11434/api/version",
          "expect": "预期输出: {\"version\":\"0.33.2\"}"
        },
        {
          "lang": "agent",
          "code": "ollama list",
          "expect": "预期输出: NAME ID SIZE MODIFIED（有已导入模型时能看到行）"
        },
        {
          "lang": "agent",
          "code": "OLLAMA_MODELS=~/.ollama/models ollama serve",
          "expect": "预期输出: server 配置日志正常（ssh key 生成、blobs: 0；默认端口 11434；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s http://127.0.0.1:11434/api/version",
          "expect": "预期输出: {\"version\":\"0.33.2\"}（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "ollama list",
          "expect": "预期输出: NAME ID SIZE MODIFIED（空表 exit 0；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf\nprintf 'FROM /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf\\n' > Modelfile-qwen\nollama create qwen2.5:0.5b -f Modelfile-qwen\nollama run qwen2.5:0.5b \"你好\"",
          "expect": "预期输出: 下载完成（491MB/58.5s）→ create 显示 copying file → success → run 出字（2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway \\\n    -v open-webui:/app/backend/data --name open-webui --restart always \\\n    -e ENABLE_SIGNUP=true \\\n    -e RAG_EMBEDDING_ENGINE=ollama \\\n    -e RAG_EMBEDDING_MODEL=nomic-embed-text \\\n    ghcr.nju.edu.cn/open-webui/open-webui:main",
          "expect": "预期输出: docker run 打印容器 ID；docker ps 里 open-webui 为 Up（2026-08-30 实测；镜像 7.16GB，ghcr 国内镜像前缀）"
        },
        {
          "lang": "agent",
          "code": "pkill -f 'ollama serve' 2>/dev/null || true\nrm -rf /usr/local/lib/ollama /usr/local/bin/ollama ~/.ollama",
          "expect": "预期输出: 命令面消失（ollama 不存在）"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.33.2",
          "max": "0.33.2"
        }
      }
    },
    {
      "name": "oh-my-claudecode",
      "slug": "oh-my-claudecode",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "5.1.0",
        "max": "5.1.0"
      },
      "license": {},
      "install": {
        "cmd": "npm i -g oh-my-claude-sisyphus@5.0.0 --no-audit --no-fund --registry=https://registry.npmmirror.com && omc --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = npm 全局安装（npmmirror，2026-08-27 本环境验证）；国外/可直连网络可去掉 --registry 参数（见指南安装段地区分支）；Claude Code 插件路径见指南方式 A"
      },
      "writes": [],
      "runtime": [],
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "summary": "团队优先的多 Agent 编排工具（Claude Code 插件形态）：角色分工、并行任务，适合团队共享 Claude Code 工作流",
      "capabilities": [
        "多会话编排",
        "任务认领/锁",
        "上下文同步",
        "Claude Code 插件",
        "npm CLI"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "agent-governance-toolkit",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "ruflo",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "code-review-graph",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "cc-switch",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-mem",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "skilldock",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "claude-code-router",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "superpowers",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "oh-my-claudecode-1",
          "title": "命令未找到（omc: command not found）"
        },
        {
          "id": "oh-my-claudecode-2",
          "title": "钩子未执行（hook 不触发 / Windows hook 报错）"
        },
        {
          "id": "oh-my-claudecode-3",
          "title": "智能体未自动委托（任务没被分发）"
        },
        {
          "id": "oh-my-claudecode-4",
          "title": "更新 / 配置异常（omc-doctor 诊断）"
        },
        {
          "id": "oh-my-claudecode-5",
          "title": "包名 / 插件名 / 命令名不一致"
        },
        {
          "id": "oh-my-claudecode-6",
          "title": "Claude Code 本体环境类报错（OOM / Homebrew 冲突 / PowerShell 执行策略）"
        },
        {
          "id": "oh-my-claudecode-7",
          "title": "同名 fork 项目混淆"
        }
      ],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/oh-my-claudecode",
        "human_en": "https://trove.xin/en/solutions/oh-my-claudecode",
        "agent": "https://trove.xin/agent/solutions/oh-my-claudecode",
        "detailed": "https://trove.xin/detailed/oh-my-claudecode"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm i -g oh-my-claude-sisyphus@5.1.0 --no-audit --no-fund --registry=https://registry.npmmirror.com\nomc --version",
          "expect": "预期输出: 5.1.0"
        },
        {
          "lang": "agent",
          "code": "npm i -g oh-my-claude-sisyphus@5.1.0 --no-audit --no-fund --registry=https://registry.npmmirror.com\nomc --version",
          "expect": "预期输出: 5.1.0"
        },
        {
          "lang": "agent",
          "code": "npm i -g oh-my-claude-sisyphus@5.1.0 --no-audit --no-fund\nomc --version",
          "expect": "预期输出: 5.1.0"
        },
        {
          "lang": "agent",
          "code": "omc config",
          "expect": "预期输出: 当前配置内容（JSON 或初始化引导提示）"
        },
        {
          "lang": "agent",
          "code": "/plugin marketplace add https://github.com/Yeachan-Heo/oh-my-claudecode\n/plugin install oh-my-claudecode\n/omc-setup",
          "expect": "预期输出: 插件列表出现 oh-my-claudecode；omc-setup 完成初始化"
        },
        {
          "lang": "agent",
          "code": "omc --version",
          "expect": "预期输出: 5.1.0"
        },
        {
          "lang": "agent",
          "code": "omc --help",
          "expect": "预期输出: 以 Usage: omc [options] [command] 开头，含 launch/interop/ask/config 等命令"
        },
        {
          "lang": "agent",
          "code": "omc config",
          "expect": "预期输出: 当前配置内容（JSON 或初始化引导提示）"
        },
        {
          "lang": "agent",
          "code": "/plan \"要做的目标\"\n/autopilot \"自动执行的目标\"\n/team",
          "expect": "预期输出: plan 输出执行计划；autopilot 开始执行；team 进入团队编排"
        },
        {
          "lang": "agent",
          "code": "omc --version",
          "expect": "预期输出: 5.1.0"
        },
        {
          "lang": "agent",
          "code": "omc --help",
          "expect": "预期输出: 以 Usage: omc [options] [command] 开头，含 launch/interop/ask/config 等命令"
        },
        {
          "lang": "agent",
          "code": "omc config",
          "expect": "预期输出: 当前配置内容（JSON 或初始化引导提示）"
        },
        {
          "lang": "agent",
          "code": "npm rm -g oh-my-claude-sisyphus || true\ncommand -v omc >/dev/null 2>&1 && echo \"omc 仍存在（方式 A 安装请执行 /plugin remove oh-my-claudecode）\" || echo \"omc removed: OK\"",
          "expect": "预期输出: omc removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "5.1.0",
          "max": "5.1.0"
        }
      }
    },
    {
      "name": "open-webui",
      "slug": "open-webui",
      "category": "ui",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.11.1",
        "max": "0.11.3"
      },
      "license": {},
      "install": {
        "cmd": "docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.nju.edu.cn/open-webui/open-webui:main",
        "interactive": false,
        "semi_auto_note": "实测命令 = ghcr.nju.edu.cn 镜像（2026-08-30 组合包拉取成功 + 2026-09-01 缓存镜像启动成功）→ 容器 healthy 约 1-2 分钟 → /health 200 → signup/signin → /api/models 见 ollama 模型 → chat 真实推理（qwen2.5-0.5b）；pip 形态实测失败：Python 3.14.4 不满足 Requires-Python <3.13.0a1,>=3.11（全部版本要求 3.11~3.12）；证据 实测复现日志（2026-09-01）"
      },
      "writes": [],
      "runtime": [],
      "domain": "",
      "domain_label": "",
      "summary": "本地大模型网页聊天界面：装在 ollama 之上，浏览器聊天、多模型切换、会话历史与本地 RAG，适合给本地模型加图形界面",
      "capabilities": [
        "浏览器网页聊天界面（会话历史、多模型切换）",
        "本地知识库 RAG（embedding 走 ollama，nomic-embed-text）",
        "OpenAI 兼容 API（/api/chat/completions，token 认证）",
        "首个注册用户自动管理员；支持注册开关",
        "管理员密码容器内 CLI 重置"
      ],
      "validated_at": "2026-09-01",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "ollama",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "open-webui-deploy-fail",
          "title": "Open WebUI 用 Docker 起不来（镜像拉不动、端口被占、页面打不开、数据丢）"
        },
        {
          "id": "open-webui-forgot-password",
          "title": "Open WebUI 忘记管理员密码 / 登录不进去怎么办"
        },
        {
          "id": "open-webui-ollama-connect",
          "title": "Docker 里跑 Open WebUI 连不上 Ollama（页面显示 Ollama 未连接 / 模型列表为空）"
        },
        {
          "id": "open-webui-rag-embedding",
          "title": "Open WebUI 知识库检索不生效 / embedding 配置错（上传文件报 Ollama embed 404）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-local-llm-finetune",
          "title": "本地大模型微调/推理环境（ollama + open-webui + LLaMA-Factory）",
          "page": "https://trove.xin/combos/combo-local-llm-finetune"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/open-webui",
        "human_en": "https://trove.xin/en/solutions/open-webui",
        "agent": "https://trove.xin/agent/solutions/open-webui",
        "detailed": "https://trove.xin/detailed/open-webui"
      },
      "guide_blocks": [],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-09-01",
        "ttl_due": "2027-02-28",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.11.1",
          "max": "0.11.3"
        }
      }
    },
    {
      "name": "openclaw",
      "slug": "openclaw",
      "category": "agent-platform",
      "tier": "complete",
      "security_level": "yellow",
      "version_range": {
        "min": "2026.8.1",
        "max": "2026.8.1"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npm install -g openclaw@latest --no-audit --no-fund",
        "interactive": false,
        "semi_auto_note": "实测 2026-09-01：v2026.8.1（npm 包，npmmirror 镜像 331 包 9s；Node >= 22.22.3/24.15.0+/25.9.0+）。实测坑：系统 Node 22.22.1 被 postinstall 拒绝 → 隔离部署 Node 24.15.0 到 /opt/node-v24.15.0-linux-x64 后安装通过。onboard（首次配置）为交互向导需模型 API key，零账号纪律未实测。证据 实测复现日志（2026-09-01）。"
      },
      "writes": [],
      "runtime": [],
      "domain": "",
      "domain_label": "",
      "summary": "自托管个人 AI 助手平台：本地 Gateway + Telegram/WhatsApp 等多通道 + cron 自动化 + 浏览器自动化，适合自部署全天候助手",
      "capabilities": [
        "本地 Gateway 控制面（会话/工具/事件/通道）",
        "多通道接入（WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage）",
        "cron/automations 定时任务与触发式自动化",
        "内置浏览器自动化（Chrome/Chromium 实例管理）",
        "跨会话记忆（memory 子命令）",
        "模型层支持 hosted + 本地 provider（可接 ollama）",
        "skills/plugins 生态（ClawHub）+ MCP 服务管理",
        "DM 配对安全模型（inbound 消息不可信，pairing 审批）"
      ],
      "validated_at": "2026-09-01",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/openclaw",
        "human_en": "https://trove.xin/en/solutions/openclaw",
        "agent": "https://trove.xin/agent/solutions/openclaw",
        "detailed": "https://trove.xin/detailed/openclaw"
      },
      "guide_blocks": [
        {
          "lang": "gent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g openclaw@latest --no-audit --no-fund && openclaw --version",
          "expect": ""
        },
        {
          "lang": "gent",
          "code": "node --version",
          "expect": ""
        },
        {
          "lang": "gent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g openclaw@latest --no-audit --no-fund",
          "expect": ""
        },
        {
          "lang": "gent",
          "code": "openclaw --version && openclaw --help | head -20",
          "expect": ""
        },
        {
          "lang": "gent",
          "code": "openclaw gateway status",
          "expect": ""
        },
        {
          "lang": "agent",
          "code": "openclaw cron list",
          "expect": ""
        },
        {
          "lang": "agent",
          "code": "openclaw browser doctor",
          "expect": ""
        },
        {
          "lang": "agent",
          "code": "openclaw channels list",
          "expect": ""
        },
        {
          "lang": "agent",
          "code": "openclaw channels list --all",
          "expect": ""
        },
        {
          "lang": "agent",
          "code": "openclaw doctor",
          "expect": ""
        },
        {
          "lang": "gent",
          "code": "npm uninstall -g openclaw --no-audit --no-fund && rm -rf ~/.openclaw",
          "expect": ""
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-09-01",
        "ttl_due": "2027-02-28",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "2026.8.1",
          "max": "2026.8.1"
        }
      }
    },
    {
      "name": "playwright-mcp",
      "slug": "playwright-mcp",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "0.0.79",
        "max": "0.0.79"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npm install -g @playwright/mcp && npx --no-install @playwright/mcp --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = npmmirror 前缀 npm install -g @playwright/mcp（2026-08-29 本环境验证，Node v22.22.1，3 秒装完）；首次运行需浏览器引擎：npx --no-install playwright install chromium（2026-08-29 实测 113.9MiB 下载成功，headless shell 冒烟通过）；免安装形态 npx -y @playwright/mcp@latest 官方支持；server 默认监听 8931 端口（--port 可改），也支持 stdio 模式"
      },
      "writes": [],
      "runtime": [],
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "summary": "MCP 浏览器控制面：Agent 通过标准协议操作与验证网页（导航/点击/填表/截图/断言）",
      "capabilities": [
        "MCP 标准协议接入（stdio / HTTP+SSE）",
        "浏览器导航/点击/填表/截图",
        "无头/有头模式",
        "页面断言与 E2E 验证",
        "权限边界开关（allowed-hosts / workspace roots）"
      ],
      "validated_at": "2026-08-29",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "vite-deploy",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "repomix",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-08-29)"
        }
      ],
      "related_problems": [],
      "related_combos": [
        {
          "id": "combo-auto-test",
          "title": "自动化测试闭环（静态分析 → 沙箱执行 → 浏览器 E2E）",
          "page": "https://trove.xin/combos/combo-auto-test"
        },
        {
          "id": "combo-web-deploy-verify",
          "title": "网页部署验证",
          "page": "https://trove.xin/combos/combo-web-deploy-verify"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/playwright-mcp",
        "human_en": "https://trove.xin/en/solutions/playwright-mcp",
        "agent": "https://trove.xin/agent/solutions/playwright-mcp",
        "detailed": "https://trove.xin/detailed/playwright-mcp"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g @playwright/mcp\nnpx --no-install @playwright/mcp --version",
          "expect": "预期输出: Version 0.0.79"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g @playwright/mcp\nnpx --no-install @playwright/mcp --version",
          "expect": "预期输出: Version 0.0.79"
        },
        {
          "lang": "agent",
          "code": "npx --no-install playwright install chromium",
          "expect": "预期输出: 浏览器引擎下载完成（2026-08-29 实测下载成功）"
        },
        {
          "lang": "agent",
          "code": "npx --no-install @playwright/mcp --port 8931 --headless --browser chromium",
          "expect": "预期输出: Listening on http://localhost:8931（客户端配置 url 指向 /mcp 端点；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8931/mcp",
          "expect": "预期输出: 非 200（拒绝非法 GET 属预期安全行为；POST JSON-RPC 才受理——2026-08-29 实测根路径 403）"
        },
        {
          "lang": "agent",
          "code": "node -e \"const {chromium}=require('@playwright/mcp/node_modules/playwright');(async=>{const b=await chromium.launch({headless:true});const p=await b.newPage;await p.goto('data:text/html,<title>ok</title>');console.log(await p.title);await b.close})\"",
          "expect": "预期输出: ok（2026-08-29 实测通过）"
        },
        {
          "lang": "agent",
          "code": "npx --no-install @playwright/mcp --version",
          "expect": "预期输出: Version 0.0.79"
        },
        {
          "lang": "agent",
          "code": "npx --no-install @playwright/mcp --port 8931 --headless --browser chromium",
          "expect": "预期输出: Listening on http://localhost:8931（客户端配置 url 指向 /mcp 端点）"
        },
        {
          "lang": "agent",
          "code": "node -e \"const {chromium}=require('@playwright/mcp/node_modules/playwright');(async=>{const b=await chromium.launch({headless:true});const p=await b.newPage;await p.goto('data:text/html,<title>ok</title>');console.log(await p.title);await b.close})\"",
          "expect": "预期输出: ok（2026-08-29 实测通过）"
        },
        {
          "lang": "agent",
          "code": "npx --no-install @playwright/mcp --port 8931 --headless --browser chromium",
          "expect": "预期输出: Listening on http://localhost:8931（客户端配置 url 指向 /mcp 端点；2026-08-29 实测）"
        },
        {
          "lang": "agent",
          "code": "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8931/mcp",
          "expect": "预期输出: 非 200（拒绝非法 GET 属预期安全行为；POST JSON-RPC 才受理——2026-08-29 实测根路径 403）"
        },
        {
          "lang": "agent",
          "code": "node -e \"const {chromium}=require('@playwright/mcp/node_modules/playwright');(async=>{const b=await chromium.launch({headless:true});const p=await b.newPage;await p.goto('data:text/html,<title>ok</title>');console.log(await p.title);await b.close})\"",
          "expect": "预期输出: ok（2026-08-29 实测通过）"
        },
        {
          "lang": "agent",
          "code": "npm uninstall -g @playwright/mcp\ncommand -v @playwright/mcp >/dev/null 2>&1 && echo \"残留: playwright-mcp 仍存在\" || echo \"playwright-mcp removed: OK\"",
          "expect": "预期输出: playwright-mcp removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "0.0.79",
          "max": "0.0.79"
        }
      }
    },
    {
      "name": "repomix",
      "slug": "repomix",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.18.0",
        "max": "1.18.0"
      },
      "license": {},
      "install": {
        "cmd": "npm_config_registry=https://registry.npmmirror.com npm install -g repomix && repomix --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = npmmirror 前缀 npm install -g repomix（2026-08-29 本环境验证，Node v22.22.1）；免安装形态 npx -y repomix 同样实测通过（npmmirror 秒装）；官方另有单二进制 Release（免 Node，推断未实测）；brew（macOS）官方支持未实测"
      },
      "writes": [],
      "runtime": [],
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "summary": "仓库打包成单一 token 控制文件：给 AI 审查/理解大仓库喂料（可压缩、限范围），适合 AI 代码审查场景",
      "capabilities": [
        "仓库打包成单文件",
        "token 预算控制（--compress / --include）",
        "默认排除 .gitignore 与二进制",
        "npx 免安装形态",
        "单二进制发布（免 Node）"
      ],
      "validated_at": "2026-08-29",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "code-review-graph",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-08-29)"
        },
        {
          "with": "swe-rex",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-08-29)"
        }
      ],
      "related_problems": [],
      "related_combos": [
        {
          "id": "combo-ai-code-review",
          "title": "AI 代码审查工作流（repomix + code-review-graph + swe-rex）",
          "page": "https://trove.xin/combos/combo-ai-code-review"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/repomix",
        "human_en": "https://trove.xin/en/solutions/repomix",
        "agent": "https://trove.xin/agent/solutions/repomix",
        "detailed": "https://trove.xin/detailed/repomix"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g repomix\nrepomix --version",
          "expect": "预期输出: 1.18.0"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npm install -g repomix\nrepomix --version",
          "expect": "预期输出: 1.18.0"
        },
        {
          "lang": "agent",
          "code": "repomix . --compress --output repomix-output.txt",
          "expect": "预期输出: Pack Summary（Total Files / Total Tokens / Output 路径；2026-08-29 实测 2 文件 → 1983 字符）"
        },
        {
          "lang": "agent",
          "code": "repomix . --compress --include \"src/**/*.ts,README.md\"",
          "expect": "预期输出: 只打包匹配的文件（--compress 实测通过；--include 为官方功能，未实测）"
        },
        {
          "lang": "agent",
          "code": "repomix . --output /tmp/repo-context.txt",
          "expect": "预期输出: 单文件写入指定路径（2026-08-29 实测 --output 生效）"
        },
        {
          "lang": "agent",
          "code": "repomix . --compress --output repomix-output.txt\nls -l repomix-output.txt",
          "expect": "预期输出: Pack Summary（Total Files / Total Tokens / Output 路径）+ 输出文件存在（2026-08-29 实测 2 文件 → 1983 字符）"
        },
        {
          "lang": "agent",
          "code": "repomix . --output /tmp/repo-context.txt\nls -l /tmp/repo-context.txt",
          "expect": "预期输出: 单文件写入指定路径（--output 实测生效）"
        },
        {
          "lang": "agent",
          "code": "repomix . --compress --output repomix-output.txt",
          "expect": "预期输出: Pack Summary（Total Files / Total Tokens / Output 路径；2026-08-29 实测 2 文件 → 1983 字符）"
        },
        {
          "lang": "agent",
          "code": "repomix . --compress --include \"src/**/*.ts,README.md\"",
          "expect": "预期输出: 只打包匹配的文件（--compress 实测通过；--include 为官方功能，未实测）"
        },
        {
          "lang": "agent",
          "code": "repomix . --output /tmp/repo-context.txt",
          "expect": "预期输出: 单文件写入指定路径（2026-08-29 实测 --output 生效）"
        },
        {
          "lang": "agent",
          "code": "npm uninstall -g repomix\ncommand -v repomix >/dev/null 2>&1 && echo \"残留: repomix 仍存在\" || echo \"repomix removed: OK\"",
          "expect": "预期输出: repomix removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.18.0",
          "max": "1.18.0"
        }
      }
    },
    {
      "name": "ruflo",
      "slug": "ruflo",
      "category": "session",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "3.38.20",
        "max": "3.38.20"
      },
      "license": {},
      "install": {
        "cmd": "npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect && npx -y ruflo --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = npx ruflo init（npmmirror，2026-08-27 本环境验证，v3.38.20；首次含原生 prebuild 下载约 5 分钟、缓存 1.6GB）；国内可加环境变量 npm_config_registry=https://registry.npmmirror.com；--no-signup/--no-skills-sh/--no-codex-detect 消除交互与外网注册；重档条目：init 写工作区 .claude/.claude-flow/.agents/.swarm/.mcp.json/CLAUDE.md + HOME ~/.claude/CLAUDE.md 指针块，doctor 会拉起后台 daemon（ruflo daemon stop 停止）；完整编排需 LLM provider"
      },
      "writes": [],
      "runtime": [],
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "summary": "Agent meta-harness：为 Claude Code/Codex 加编排、记忆与 swarm",
      "capabilities": [
        "100+ 特化 agent",
        "swarm 编排",
        "自学习记忆",
        "MCP 服务器",
        "后台 daemon"
      ],
      "validated_at": "2026-08-27",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "Evidence details kept locally"
        }
      ],
      "related_problems": [
        {
          "id": "ruflo-1",
          "title": "`ruflo plugins install` 命令不存在（正确技能入口是 npx skills add）"
        },
        {
          "id": "ruflo-2",
          "title": "ruflo 安装被未发布依赖 pin 卡住（@claude-flow/mcp alpha 未发布）"
        },
        {
          "id": "ruflo-3",
          "title": "Windows 上 `hive-mind spawn --claude` 检测不到 claude CLI（PATH 检测失败）"
        },
        {
          "id": "ruflo-4",
          "title": "首次 npx ruflo init 很慢、npx 缓存约 2GB（磁盘敏感环境）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        },
        {
          "id": "combo-multi-agent-workflow",
          "title": "多 Agent 工作流编排",
          "page": "https://trove.xin/combos/combo-multi-agent-workflow"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/ruflo",
        "human_en": "https://trove.xin/en/solutions/ruflo",
        "agent": "https://trove.xin/agent/solutions/ruflo",
        "detailed": "https://trove.xin/detailed/ruflo"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect\nnpx -y ruflo --version",
          "expect": "预期输出: ruflo v3.38.20"
        },
        {
          "lang": "agent",
          "code": "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect\nnpx -y ruflo --version",
          "expect": "预期输出: ruflo v3.38.20"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect\nnpx -y ruflo --version",
          "expect": "预期输出: ruflo v3.38.20"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo doctor",
          "expect": "预期输出: RuFlo Doctor 检查项（版本/Node/npm/Git/配置/Memory DB/MCP）；⚠ 项为警告不阻塞；用完后 ruflo daemon stop"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo providers configure -p ollama -e http://127.0.0.1:11434 -m qwen2.5-0.5b\nnpx -y ruflo providers test -p ollama\nnpx -y ruflo swarm init\nnpx -y ruflo swarm start -o \"最小目标\"\nnpx -y ruflo swarm status",
          "expect": "预期输出: Configured: ollama（endpoint 保存）→ Provider Connectivity Test：Ollama Connected at 11434 PASS → Swarm initialized（Swarm ID + hierarchical/15 agents）→ Agent Deployment Plan（coordinator/architect/coder×3/tester×2/reviewer）+ Swarm initialized with 8 agent slots → 状态表（Elapsed 0s）"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo --version\nls -d .claude-flow 2>/dev/null && echo \"skeleton: OK\" || echo \"skeleton: MISSING\"",
          "expect": "预期输出: ruflo v3.38.20；skeleton: OK"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo doctor",
          "expect": "预期输出: RuFlo Doctor 检查项（版本/Node/npm/Git/配置/Memory DB/MCP）；⚠ 项为警告不阻塞"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo --help",
          "expect": "预期输出: 以 Ruflo v3.38.20 开头，含 init/start/agent/swarm/memory/mcp/hooks/daemon 等 30+ 命令"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo doctor",
          "expect": "预期输出: RuFlo Doctor 检查项（版本/Node/npm/Git/配置/Memory DB/MCP）；⚠ 项为警告不阻塞；用完后 ruflo daemon stop"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo daemon stop",
          "expect": "预期输出: Worker daemon stopped"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo start",
          "expect": "预期输出: 编排系统启动状态（swarm/agents/memory 就绪；实测 2026-08-30：providers configure/test 本地端点连通 PASS → swarm init → swarm start → swarm status 全过，证据 实测复现日志（2026-08-30））"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo swarm status",
          "expect": "预期输出: 状态表（Performance Metrics：Elapsed Time 0s / Success Rate no data；Coordination：Messages Sent 0 / Consensus Rounds 0——任务未跑时多列为 no data / 0 属正常）"
        },
        {
          "lang": "agent",
          "code": "npx -y ruflo swarm stop <Swarm ID>",
          "expect": "预期输出: swarm 停止确认（实测：不带 ID 直接 `ruflo swarm stop` 报 `[ERROR] Swarm ID is required`；Swarm ID 用 `swarm status` 或 init 输出查）"
        },
        {
          "lang": "agent",
          "code": "npm rm -g ruflo || true\nrm -rf -- .claude-flow .swarm ruvector.db 2>/dev/null || true\ncommand -v ruflo >/dev/null 2>&1 && echo \"ruflo 仍存在\" || echo \"ruflo removed: OK\"",
          "expect": "预期输出: ruflo removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-27",
        "ttl_due": "2027-02-23",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "3.38.20",
          "max": "3.38.20"
        }
      }
    },
    {
      "name": "skilldock",
      "slug": "skilldock",
      "category": "plugin-mgmt",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.0.15",
        "max": "1.0.15"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o SkillDock_x64-setup.exe https://gh-proxy.com/https://github.com/wanghuan9/skilldock/releases/download/v1.0.15/SkillDock_1.0.15_x64-setup.exe && ./SkillDock_x64-setup.exe",
        "interactive": true,
        "semi_auto_note": "官方资产 v1.0.15（2026-08-19，6 资产）仅 Windows exe + macOS dmg/app——无 Linux 二进制（本环境 WSL 未实测运行）；latest.json 元数据 + README 结构审计确认；桌面 GUI 形态；管理面 = 各 agent 技能目录（Claude Code/Cursor/Codex/Windsurf/Gemini CLI/Copilot）+ MCP 配置 + 插件；Git 感知更新（追踪上游 + 本地修改）；安装 = 桌面应用安装向导"
      },
      "writes": [],
      "runtime": [],
      "domain": "skills",
      "domain_label": "技能管理",
      "summary": "AI 技能管理器桌面应用（Claude Code/Cursor/Codex 等技能安装、组织、Git 同步）",
      "capabilities": [
        "技能库管理（安装/更新/删除）",
        "多工具技能同步（一键）",
        "MCP 服务器管理",
        "插件管理",
        "Git 感知更新",
        "冲突条目识别"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/skilldock",
        "human_en": "https://trove.xin/en/solutions/skilldock",
        "agent": "https://trove.xin/agent/solutions/skilldock",
        "detailed": "https://trove.xin/detailed/skilldock"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o SkillDock_x64-setup.exe https://gh-proxy.com/https://github.com/wanghuan9/skilldock/releases/download/v1.0.15/SkillDock_1.0.15_x64-setup.exe\n./SkillDock_x64-setup.exe",
          "expect": "预期输出: 安装向导（默认下一步完成）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o SkillDock_x64-setup.exe https://gh-proxy.com/https://github.com/wanghuan9/skilldock/releases/download/v1.0.15/SkillDock_1.0.15_x64-setup.exe\n./SkillDock_x64-setup.exe",
          "expect": "预期输出: 安装向导（默认下一步完成）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o SkillDock.dmg https://gh-proxy.com/https://github.com/wanghuan9/skilldock/releases/download/v1.0.15/SkillDock_1.0.15_aarch64.dmg\nopen SkillDock.dmg",
          "expect": "预期输出: 拖入 Applications"
        },
        {
          "lang": "agent",
          "code": "# GUI：Skills → Install → 粘贴 Git 地址",
          "expect": "预期输出: 技能入库并可选同步到目标工具"
        },
        {
          "lang": "agent",
          "code": "# GUI：技能详情 → 勾选 Claude Code/Codex/Cursor → Sync",
          "expect": "预期输出: 技能分发到所选工具（避免手抄文件）"
        },
        {
          "lang": "agent",
          "code": "# GUI：技能列表 → 查看上游更新/本地修改 → 预览 → Update/Push",
          "expect": "预期输出: 更新前预览 diff，确认后应用"
        },
        {
          "lang": "agent",
          "code": "# GUI：打开 SkillDock，确认主界面与已扫描技能列表",
          "expect": "预期输出: 应用主界面 + 本机工具技能目录列表"
        },
        {
          "lang": "agent",
          "code": "# GUI：Skills → Install → 粘贴 Git 地址 → 勾选目标工具 → Sync",
          "expect": "预期输出: 技能入库 + 分发到所选工具目录"
        },
        {
          "lang": "agent",
          "code": "# GUI：打开应用 → 自动扫描 Claude Code/Cursor/Codex/Windsurf 等技能目录",
          "expect": "预期输出: 已管理/未管理/冲突条目分组列表"
        },
        {
          "lang": "agent",
          "code": "# GUI：Skills → Install → 粘贴 Git 地址",
          "expect": "预期输出: 技能入库并可选同步到目标工具"
        },
        {
          "lang": "agent",
          "code": "# GUI：技能详情 → 勾选 Claude Code/Codex/Cursor → Sync",
          "expect": "预期输出: 技能分发到所选工具（避免手抄文件）"
        },
        {
          "lang": "agent",
          "code": "# GUI：技能列表 → 查看上游更新/本地修改 → 预览 → Update/Push",
          "expect": "预期输出: 更新前预览 diff，确认后应用"
        },
        {
          "lang": "agent",
          "code": "# Windows：设置 → 应用 → 卸载 SkillDock；macOS：拖废纸篓\nrm -rf ~/.config/SkillDock ~/.skilldock 2>/dev/null || true\nls ~/.config/SkillDock ~/.skilldock 2>/dev/null | grep -q . && echo '残留: SkillDock 配置仍在' || echo 'skilldock removed: OK'",
          "expect": "预期输出: skilldock removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.0.15",
          "max": "1.0.15"
        }
      }
    },
    {
      "name": "skillspector",
      "slug": "skillspector",
      "category": "security",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "2.10.0",
        "max": "2.10.0"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl && skillspector --version",
        "interactive": false,
        "semi_auto_note": "实测命令 = release wheel 安装（2026-08-28 本环境 venv 验证，v2.10.0，gh-proxy 下载 wheel 476KB；pip 依赖走清华镜像）；注意：skillspector 不在 PyPI（PyPI/清华镜像均 404，pip install skillspector 必失败）——发行渠道 = GitHub Release wheel；CLI = skillspector scan/mcp/baseline；静态扫描用 --no-llm 免 API key；LLM 语义分析（semantic_* 分析器）需 OPENAI_API_KEY 或 SKILLSPECTOR_PROVIDER（可选）；无后台进程"
      },
      "writes": [],
      "runtime": [],
      "domain": "security",
      "domain_label": "安全与治理",
      "summary": "AI agent 技能安全扫描器：安装前检测漏洞、恶意模式与安全配置（NVIDIA 出品）",
      "capabilities": [
        "技能漏洞扫描（SC2 外部脚本拉取等）",
        "恶意模式检测",
        "工具链滥用检测",
        "MCP 服务器模式",
        "LLM 语义分析（可选）",
        "终端/JSON/Markdown 报告"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "skillspector-1",
          "title": "pip install skillspector 找不到包（No matching distribution）"
        },
        {
          "id": "skillspector-2",
          "title": "Failed building wheel for yara-python（构建依赖缺失）"
        },
        {
          "id": "skillspector-3",
          "title": "MCP 模式 stdio 挂起（可选依赖缺失）"
        },
        {
          "id": "skillspector-4",
          "title": "依赖版本冲突警告"
        },
        {
          "id": "skillspector-5",
          "title": "风险评分 >50 输出 DO NOT INSTALL"
        },
        {
          "id": "skillspector-6",
          "title": "semantic_* 分析器警告（缺 API key）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-security-preflight",
          "title": "安全前置（agentshield + skillspector）",
          "page": "https://trove.xin/combos/combo-security-preflight"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/skillspector",
        "human_en": "https://trove.xin/en/solutions/skillspector",
        "agent": "https://trove.xin/agent/solutions/skillspector",
        "detailed": "https://trove.xin/detailed/skillspector"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl\nskillspector --version",
          "expect": "预期输出: 2.10.0（无 --version 标志时以 --help 为准）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl\nskillspector --version",
          "expect": "预期输出: 2.10.0（无 --version 标志时以 --help 为准）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan ./skills/my-skill --no-llm",
          "expect": "预期输出: SkillSpector Security Report（Score/Severity/Components/Issues 清单，含 SC2 外部脚本拉取、TM2 工具链滥用等检出）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan ./skills/my-skill",
          "expect": "预期输出: 静态 + 语义分析报告（semantic_developer_intent/quality_policy/security_discovery 分析器；无 key 时自动跳过并警告）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan https://github.com/owner/repo",
          "expect": "预期输出: 同报告格式（Source 显示远端地址）"
        },
        {
          "lang": "agent",
          "code": "skillspector mcp",
          "expect": "预期输出: MCP server 启动（供 agent 工具调用）"
        },
        {
          "lang": "agent",
          "code": "skillspector --version\nskillspector scan ./skills/my-skill --no-llm",
          "expect": "预期输出: 2.10.0；SkillSpector Security Report（Score/Severity/Components/Issues 清单）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan ./skills/my-skill",
          "expect": "预期输出: 静态 + 语义分析报告（semantic_developer_intent/quality_policy/security_discovery；无 key 时自动跳过并警告）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan ./skills/my-skill --no-llm",
          "expect": "预期输出: SkillSpector Security Report（Score/Severity/Components/Issues 清单，含 SC2 外部脚本拉取、TM2 工具链滥用等检出）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan ./skills/my-skill",
          "expect": "预期输出: 静态 + 语义分析报告（semantic_developer_intent/quality_policy/security_discovery 分析器；无 key 时自动跳过并警告）"
        },
        {
          "lang": "agent",
          "code": "skillspector scan https://github.com/owner/repo",
          "expect": "预期输出: 同报告格式（Source 显示远端地址）"
        },
        {
          "lang": "agent",
          "code": "skillspector mcp",
          "expect": "预期输出: MCP server 启动（供 agent 工具调用）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y skillspector 2>/dev/null || true\ncommand -v skillspector >/dev/null 2>&1 && echo '残留: skillspector 仍存在' || echo 'skillspector removed: OK'",
          "expect": "预期输出: skillspector removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "2.10.0",
          "max": "2.10.0"
        }
      }
    },
    {
      "name": "superpowers",
      "slug": "superpowers",
      "category": "skills",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "6.3.0",
        "max": "6.3.0"
      },
      "license": {},
      "install": {
        "cmd": "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 && rm -rf /tmp/superpowers-src && mkdir -p /tmp/superpowers-src && tar xzf /tmp/superpowers.tar.gz -C /tmp/superpowers-src --strip-components=1 && mkdir -p ~/.codex/skills && cp -r /tmp/superpowers-src/skills ~/.codex/skills/superpowers && test -f ~/.codex/skills/superpowers/brainstorming/SKILL.md && echo 'superpowers installed: OK'",
        "interactive": false,
        "semi_auto_note": "实测命令 = tarball 下载 + 复制 skills 到 ~/.codex/skills/superpowers（2026-08-27 本环境验证，v6.3.0，544K/2s）；国内直连 GitHub codeload 可用（1.7s），无需镜像；重复安装覆盖同名目录；Claude Code 形态（官方插件市场）未实测（本机未登录）"
      },
      "writes": [],
      "runtime": [],
      "domain": "skills",
      "domain_label": "技能管理",
      "summary": "Agent 技能框架 + 软件开发方法论（14 个可组合技能），适合用 AI 结对开发、想要结构化工作流的开发者",
      "capabilities": [
        "14 个可组合技能",
        "TDD/调试/子代理驱动开发",
        "多 harness 插件形态",
        "方法论引导",
        "无运行时依赖"
      ],
      "validated_at": "2026-08-27",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "oh-my-claudecode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "agent-governance-toolkit",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "deepcode",
          "state": "possible-compatible",
          "risk": null,
          "evidence": "site docs"
        },
        {
          "with": "ruflo",
          "state": "possible-conflict",
          "risk": null,
          "evidence": "site docs"
        }
      ],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        },
        {
          "id": "superpowers-1",
          "title": "/plugin install 报 fatal: Failed to connect to github.com port 443"
        },
        {
          "id": "superpowers-2",
          "title": "ZIP / tarball 下载超时或中断"
        },
        {
          "id": "superpowers-3",
          "title": "Plugin not found"
        },
        {
          "id": "superpowers-4",
          "title": "安装后 hook error（Windows SessionStart hook fails）"
        },
        {
          "id": "superpowers-5",
          "title": "装完看不到技能"
        },
        {
          "id": "superpowers-6",
          "title": "本地/离线部署后插件显示不可用（感叹号）"
        },
        {
          "id": "superpowers-7",
          "title": "无本地文件读取权限 / 渠道不一致"
        }
      ],
      "related_combos": [
        {
          "id": "combo-env-init",
          "title": "Agent 开发环境一键初始化",
          "page": "https://trove.xin/combos/combo-env-init"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/superpowers",
        "human_en": "https://trove.xin/en/solutions/superpowers",
        "agent": "https://trove.xin/agent/solutions/superpowers",
        "detailed": "https://trove.xin/detailed/superpowers"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0\nrm -rf /tmp/superpowers-src && mkdir -p /tmp/superpowers-src\ntar xzf /tmp/superpowers.tar.gz -C /tmp/superpowers-src --strip-components=1\nmkdir -p ~/.codex/skills && cp -r /tmp/superpowers-src/skills ~/.codex/skills/superpowers\ntest -f ~/.codex/skills/superpowers/brainstorming/SKILL.md && echo \"superpowers installed: OK\"",
          "expect": "预期输出: superpowers installed: OK"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0\nrm -rf /tmp/superpowers-src && mkdir -p /tmp/superpowers-src\ntar xzf /tmp/superpowers.tar.gz -C /tmp/superpowers-src --strip-components=1\nmkdir -p ~/.codex/skills && cp -r /tmp/superpowers-src/skills ~/.codex/skills/superpowers\ntest -f ~/.codex/skills/superpowers/brainstorming/SKILL.md && echo \"superpowers installed: OK\"",
          "expect": "预期输出: superpowers installed: OK"
        },
        {
          "lang": "agent",
          "code": "ls ~/.codex/skills/superpowers",
          "expect": "预期输出: 14 个技能目录（brainstorming / test-driven-development / systematic-debugging / subagent-driven-development 等）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 && rm -rf /tmp/superpowers-src && mkdir -p /tmp/superpowers-src && tar xzf /tmp/superpowers.tar.gz -C /tmp/superpowers-src --strip-components=1 && cp -r /tmp/superpowers-src/skills ~/.codex/skills/superpowers && echo \"updated: OK\"",
          "expect": "预期输出: updated: OK"
        },
        {
          "lang": "agent",
          "code": "ls ~/.claude/skills/superpowers 2>/dev/null || echo \"未安装 Claude 形态\"",
          "expect": "预期输出: 目录列表或未安装提示"
        },
        {
          "lang": "agent",
          "code": "test -f ~/.codex/skills/superpowers/brainstorming/SKILL.md && echo \"installed: OK\" || echo \"installed: MISSING\"\nls ~/.codex/skills/superpowers",
          "expect": "预期输出: installed: OK + 14 个技能目录（brainstorming / test-driven-development / systematic-debugging / subagent-driven-development 等）"
        },
        {
          "lang": "agent",
          "code": "# 无命令：新建会话让 Agent 触发技能即可",
          "expect": "预期输出: Agent 会话中技能被自动引用（无需手动调用）"
        },
        {
          "lang": "agent",
          "code": "ls ~/.codex/skills/superpowers",
          "expect": "预期输出: 14 个技能目录（brainstorming / test-driven-development / systematic-debugging / subagent-driven-development 等）"
        },
        {
          "lang": "agent",
          "code": "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 && rm -rf /tmp/superpowers-src && mkdir -p /tmp/superpowers-src && tar xzf /tmp/superpowers.tar.gz -C /tmp/superpowers-src --strip-components=1 && cp -r /tmp/superpowers-src/skills ~/.codex/skills/superpowers && echo \"updated: OK\"",
          "expect": "预期输出: updated: OK"
        },
        {
          "lang": "agent",
          "code": "ls ~/.claude/skills/superpowers 2>/dev/null || echo \"未安装 Claude 形态\"",
          "expect": "预期输出: 目录列表或未安装提示"
        },
        {
          "lang": "agent",
          "code": "rm -rf ~/.codex/skills/superpowers\ntest ! -d ~/.codex/skills/superpowers && echo \"superpowers removed: OK\" || echo \"残留: superpowers 仍存在\"",
          "expect": "预期输出: superpowers removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-27",
        "ttl_due": "2027-02-23",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "6.3.0",
          "max": "6.3.0"
        }
      }
    },
    {
      "name": "swe-rex",
      "slug": "swe-rex",
      "category": "runtime",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "1.4.0",
        "max": "1.4.0"
      },
      "license": {},
      "install": {
        "cmd": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0 && swerex-remote --help",
        "interactive": false,
        "semi_auto_note": "实测命令 = pip install -i 清华镜像 swe-rex==1.4.0（2026-08-28 本环境 venv 验证，Python 3.14）；CLI = swerex-remote（沙箱执行服务器，--host/--port/--auth-token）；真实沙箱执行依赖 Docker（本环境未实测——无 Docker 执行沙箱）；release v1.4.0（2025-08-14 偏旧）无资产，分发走 PyPI；库形态（swerex Python API）可编程接入"
      },
      "writes": [],
      "runtime": [],
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "summary": "Agent 沙箱化代码执行（本地/云端，可并行，SWE-agent 出品）：给编码 Agent 一个安全环境跑命令与测试",
      "capabilities": [
        "沙箱化代码执行",
        "远程/本地执行服务器",
        "并行执行",
        "Python API 接入",
        "认证 token 保护"
      ],
      "validated_at": "2026-08-28",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "agent-toolchain-coexist",
          "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-ai-code-review",
          "title": "AI 代码审查工作流（repomix + code-review-graph + swe-rex）",
          "page": "https://trove.xin/combos/combo-ai-code-review"
        },
        {
          "id": "combo-auto-test",
          "title": "自动化测试闭环（静态分析 → 沙箱执行 → 浏览器 E2E）",
          "page": "https://trove.xin/combos/combo-auto-test"
        },
        {
          "id": "combo-cicd-quality",
          "title": "CI/CD 质量门禁（swe-rex + deepcode + code-review-graph）",
          "page": "https://trove.xin/combos/combo-cicd-quality"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/swe-rex",
        "human_en": "https://trove.xin/en/solutions/swe-rex",
        "agent": "https://trove.xin/agent/solutions/swe-rex",
        "detailed": "https://trove.xin/detailed/swe-rex"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0\nswerex-remote --help",
          "expect": "预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN"
        },
        {
          "lang": "agent",
          "code": "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0\nswerex-remote --help",
          "expect": "预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import swerex; print('swerex OK')\"",
          "expect": "预期输出: swerex OK"
        },
        {
          "lang": "agent",
          "code": "swerex-remote --host 127.0.0.1 --port 8000 --auth-token <token>",
          "expect": "预期输出: 服务器监听（供 agent 客户端连接执行代码）"
        },
        {
          "lang": "agent",
          "code": "python3 - <<'PY'\nimport asyncio\nfrom swerex import RemoteSandbox\nasync def main:\n    sandbox = RemoteSandbox(host=\"127.0.0.1\", port=8000, auth_token=\"<token>\")\n    print(await sandbox.run(\"echo hi\"))\nasyncio.run(main)\nPY",
          "expect": "预期输出: hi（需先启动服务器；本环境未实测——无 Docker）"
        },
        {
          "lang": "agent",
          "code": "swerex-remote --help\npython3 -c \"import swerex; print('swerex OK')\"",
          "expect": "预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN；swerex OK"
        },
        {
          "lang": "agent",
          "code": "swerex-remote --host 127.0.0.1 --port 8000 --auth-token <token>",
          "expect": "预期输出: 服务器监听（供 agent 客户端连接执行代码）"
        },
        {
          "lang": "agent",
          "code": "python3 - <<'PY'\nimport asyncio\nfrom swerex import RemoteSandbox\nasync def main:\n    sandbox = RemoteSandbox(host=\"127.0.0.1\", port=8000, auth_token=\"<token>\")\n    print(await sandbox.run(\"echo hi\"))\nasyncio.run(main)\nPY",
          "expect": "预期输出: hi（需先启动服务器；本环境无 Docker 未实测）"
        },
        {
          "lang": "agent",
          "code": "swerex-remote --help",
          "expect": "预期输出: 服务器参数面（--host/--port/--auth-token）"
        },
        {
          "lang": "agent",
          "code": "python3 -c \"import swerex; print('swerex OK')\"",
          "expect": "预期输出: swerex OK"
        },
        {
          "lang": "agent",
          "code": "swerex-remote --host 127.0.0.1 --port 8000 --auth-token <token>",
          "expect": "预期输出: 服务器监听（供 agent 客户端连接执行代码）"
        },
        {
          "lang": "agent",
          "code": "python3 - <<'PY'\nimport asyncio\nfrom swerex import RemoteSandbox\nasync def main:\n    sandbox = RemoteSandbox(host=\"127.0.0.1\", port=8000, auth_token=\"<token>\")\n    print(await sandbox.run(\"echo hi\"))\nasyncio.run(main)\nPY",
          "expect": "预期输出: hi（需先启动服务器；本环境未实测——无 Docker）"
        },
        {
          "lang": "agent",
          "code": "pip uninstall -y swe-rex 2>/dev/null || true\ncommand -v swerex-remote >/dev/null 2>&1 && echo '残留: swerex-remote 仍存在' || echo 'swe-rex removed: OK'",
          "expect": "预期输出: swe-rex removed: OK"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-28",
        "ttl_due": "2027-02-24",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "1.4.0",
          "max": "1.4.0"
        }
      }
    },
    {
      "name": "uptime-kuma",
      "slug": "uptime-kuma",
      "category": "monitor",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "2.5.3",
        "max": "2.5.3"
      },
      "license": {},
      "install": {
        "cmd": "docker run -d --name uptime-kuma --restart=always -p 3001:3001 -v uptime-kuma:/app/data ghcr.nju.edu.cn/louislam/uptime-kuma:2.5.3",
        "interactive": false,
        "semi_auto_note": "实测命令 = ghcr.nju.edu.cn 镜像（2026-09-01 拉取 2.48GB 成功）→ 容器 12s 转 healthy → 首次访问 /setup 初始化数据库（POST /setup-database {\"dbConfig\":{\"type\":\"sqlite\"}} → 200）→ socket setup 建管理员 → login → add 监控器 → 心跳验证 UP/DOWN 正确；证据 实测复现日志（2026-09-01）"
      },
      "writes": [],
      "runtime": [],
      "domain": "",
      "domain_label": "",
      "summary": "自托管服务可用性监控：心跳探测 HTTP/TCP/Ping，服务挂了第一时间告警，带状态页，适合自建服务运维",
      "capabilities": [
        "HTTP(S)/TCP/Ping/DNS/关键字等 12 种监控器（HTTP 实测）",
        "周期性心跳探测 + 历史状态查询（interval 可配，实测 30s）",
        "公开状态页（/status，未实测）",
        "通知渠道：Telegram/Webhook/邮件等（未实测，零账号）",
        "多用户 + 权限（管理员已实测创建/登录）"
      ],
      "validated_at": "2026-09-01",
      "evidence_refs": [],
      "conflicts": [
        {
          "with": "open-webui",
          "state": "compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-09-01)"
        },
        {
          "with": "ollama",
          "state": "compatible",
          "risk": null,
          "evidence": "Evidence details kept locally (2026-09-01)"
        }
      ],
      "related_problems": [],
      "related_combos": [],
      "pages": {
        "human_zh": "https://trove.xin/solutions/uptime-kuma",
        "human_en": "https://trove.xin/en/solutions/uptime-kuma",
        "agent": "https://trove.xin/agent/solutions/uptime-kuma",
        "detailed": "https://trove.xin/detailed/uptime-kuma"
      },
      "guide_blocks": [],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-09-01",
        "ttl_due": "2027-02-28",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "2.5.3",
          "max": "2.5.3"
        }
      }
    },
    {
      "name": "vite-deploy",
      "slug": "vite-deploy",
      "category": "build",
      "tier": "complete",
      "security_level": "green",
      "version_range": {
        "min": "5.0",
        "max": "8.2"
      },
      "license": {},
      "install": {
        "cmd": "npm install --save-dev vite",
        "interactive": false,
        "semi_auto_note": "实测 2026-08-29：registry.npmmirror.com 镜像 7s 装 12 包；node v22 + vite 5.4.21 构建通过。ChunkLoadError/版本错位方案见 站内文档。"
      },
      "writes": [],
      "runtime": [],
      "domain": "deploy",
      "domain_label": "构建与部署",
      "summary": "前端构建与部署工具（Vite）：内容 hash 资源与零配置构建，并治理发布后白屏（ChunkLoadError 版本错位）",
      "capabilities": [
        "零配置构建（Vite 5）",
        "内容 hash 静态资源",
        "开发服务器热更新",
        "版本错位排查（ChunkLoadError）",
        "发布治理：先资源后入口"
      ],
      "validated_at": "2026-08-31",
      "evidence_refs": [],
      "conflicts": [],
      "related_problems": [
        {
          "id": "css-load-failure",
          "title": "CSS 样式加载失败（页面无样式/布局错乱，控制台可能无报错）"
        },
        {
          "id": "vite-chunkloaderror",
          "title": "前端发布后偶发白屏（ChunkLoadError / 版本错位）"
        },
        {
          "id": "vite-history-fallback",
          "title": "Vue3 + Vite history 路由部署后刷新白屏（404）"
        }
      ],
      "related_combos": [
        {
          "id": "combo-web-deploy-verify",
          "title": "网页部署验证",
          "page": "https://trove.xin/combos/combo-web-deploy-verify"
        }
      ],
      "pages": {
        "human_zh": "https://trove.xin/solutions/vite-deploy",
        "human_en": "https://trove.xin/en/solutions/vite-deploy",
        "agent": "https://trove.xin/agent/solutions/vite-deploy",
        "detailed": "https://trove.xin/detailed/vite-deploy"
      },
      "guide_blocks": [
        {
          "lang": "agent",
          "code": "npm install --save-dev vite --registry=https://registry.npmmirror.com",
          "expect": "预期输出: vite 8.x 装入 node_modules（2026-08-31 重测实测 8.2.2；收录实测 5.4.21）"
        },
        {
          "lang": "agent",
          "code": "npm install --save-dev vite --registry=https://registry.npmmirror.com",
          "expect": "预期输出: vite 8.x 装入 node_modules（2026-08-31 重测实测 8.2.2；收录实测 5.4.21）"
        },
        {
          "lang": "agent",
          "code": "npx vite build",
          "expect": "预期输出: dist/ 产出 index.html + assets/*-<hash>.js/css（内容 hash）"
        },
        {
          "lang": "agent",
          "code": "# 发布顺序：1) 上传 assets/*-<hash>.*  2) 再更新 index.html（旧资源保留一个发布周期）\n# nginx：try_files 先匹配真实静态文件，再回退 index.html（不能吞 404）",
          "expect": "预期输出: 部署后旧用户刷新不再白屏（完整方案见 站内文档）"
        },
        {
          "lang": "agent",
          "code": "npx vite build",
          "expect": "预期输出: 构建成功，dist/ 产出 index.html + assets/*-<hash>.js/css"
        },
        {
          "lang": "agent",
          "code": "npx vite build",
          "expect": "预期输出: dist/ 产出 index.html + assets/*-<hash>.js/css（内容 hash；实测 v8.2.2：built in 38ms，dist/assets/index-DaJps1dT.js）"
        },
        {
          "lang": "agent",
          "code": "npx vite dev --host 0.0.0.0 --port 5173",
          "expect": "预期输出: `VITE v8.2.2  ready in ~200 ms` + `Local: http://localhost:5173/`（实测 2026-09-01：25173 端口固定成功，页面返回注入 `/@vite/client` 的 HTML；改 src/main.js 后日志出现 `page reload src/main.js`，浏览器自动刷新）"
        },
        {
          "lang": "agent",
          "code": "npx vite preview --host 0.0.0.0 --port 4173",
          "expect": "预期输出: `Local: http://localhost:4173/`，访问即 dist/ 产物（实测 2026-09-01：24173 端口固定成功，curl 返回与 build 产物一致的页面；部署前用它模拟线上环境，验证白屏/路径问题）"
        },
        {
          "lang": "agent",
          "code": "npm uninstall vite",
          "expect": "预期输出: vite 从 node_modules/package.json 移除"
        }
      ],
      "agent_facts": {},
      "agent_available": true,
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-31",
        "ttl_due": "2027-02-27",
        "ttl_days": 180,
        "evidence_refs": [],
        "version_range": {
          "min": "5.0",
          "max": "8.2"
        }
      }
    }
  ],
  "problems": [
    {
      "id": "agent-framework-2",
      "title": "Python 3.14 装 agent-framework 全量卡死：resolution-too-deep（依赖图过深）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "agent-framework"
      ],
      "evidence": "site docs#2;local repro log 2026-08-29;local repro log 2026-08-29;local repro log 2026-08-29",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "在 Python 3.14 环境装 `agent-framework`，pip 一路卡在依赖解析：进度条不动、CPU 空转，最后或报 `resolution-too-deep`（Dependency resolution exceeded maximum depth），或拖到怀疑网络。你以为是源慢/包大，实际上是 **依赖图太深，解析器收敛不了**——这是解析策略问题，不是网络问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：全量 meta 包 `agent-framework` 会拉起 core[all]（fastapi/starlette 一串 Web 依赖），依赖图深度超过 pip 解析上限；**不是轮子缺失**——核心依赖 pydantic-core 2.46.5 实际有 cp314 轮子。",
          "",
          "链路：`pip install agent-framework` → 展开 meta 包全量依赖 → 解析器做深度搜索 → 图过深未收敛 → `resolution-too-deep`。断点：解析策略的深度上限 vs 巨型依赖树——2.14 版依赖图在 Python 3.14 组合下触发。",
          "",
          "实测（2026-08-29，Python 3.14.4 venv + 清华镜像，）：全量解析约 20 分钟未收敛；同一环境换装最小核心 `agent-framework-core==1.16.0` → 31MB、9 依赖装成，`from agent_framework import Agent` 导入验证通过。",
          "",
          "推论：装「大 meta 包」的通用路径 = 先装最小核心验证，再按需往上加 extras；生产环境优先官方支持组合（Python 3.10–3.13）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：装最小核心（跳过全量 meta 包的深依赖图）",
          "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework-core",
          "# 验证导入：",
          "python -c \"from agent_framework import Agent; print(Agent)\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先装最小核心 — **必做**",
          "",
          "`pip install agent-framework-core`（清华镜像）；实测 31MB、9 依赖，装成速度正常。",
          "",
          "### 2. 导入验证 — **必做**",
          "",
          "`python -c \"from agent_framework import Agent; print(Agent)\"` 输出类对象即成功。",
          "",
          "### 3. 需要全量/扩展时 — **按场景**",
          "",
          "优先在 Python 3.10–3.13 环境装全量 meta 包；3.14 用户按报错逐个补带扩展的依赖，或只用核心 + 具体 provider 包（openai/ollama 等）。",
          "",
          "### 4. 构建类失败时 — **排查**",
          "",
          "若报的是编译/构建失败（而非解析超深），检查 `build-essential`、Rust 工具链等构建依赖。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `pip install agent-framework-core` 正常完成（无长卡）；",
          "- 导入 `Agent` 成功；",
          "- provider 面（如 `agent_framework.openai`）按需导入可用。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- Python 3.10–3.13：官方支持组合，全量包也可装（推荐）；",
          "- CI：先装 core 跑单测，全量安装放独立 job，避免 20 分钟级解析卡整个流水线；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "Python 3.14 环境安装报错或长时间卡在构建阶段；部分包（如带 Rust/C 扩展的）无 cp314 轮子，进入源码编译。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "官方支持范围 Python 3.10-3.13；3.14 较新，部分依赖没有预编译轮子（2026-08-28 本环境实测信号），编译耗时且可能失败。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 官方支持环境：用 Python 3.10-3.13 的 venv/conda 装（最稳）。",
          "2. 3.14 用户：装 `agent-framework-core` 最小集（不加 [all]）先验证核心可用；带扩展依赖按报错逐个处理。",
          "3. 编译失败时检查缺失构建工具：`build-essential`、Rust 工具链（如适用）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 2026-08-29 实测复现（Python 3.14.4 venv + 清华镜像，实测复现日志（2026-08-29））：全量 meta 包解析约 20 分钟未收敛，pip 报 `resolution-too-deep`（Dependency resolution exceeded maximum depth）——依赖图过深而非轮子缺失：核心依赖 pydantic-core 2.46.5 有 cp314 轮子。规避：装 `agent-framework-core` 最小核心（实测成功，31MB，9 依赖）。",
          "- 2026-08-28 本环境实测：Python 3.14.4 全量安装失败/卡编译（部分依赖无预编译轮子信号）。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-framework-2",
        "agent": "https://trove.xin/agent/problems/agent-framework-2"
      }
    },
    {
      "id": "agent-framework-3",
      "title": "MCP 连接只显示 Cancelled via...（真实 401 认证错误被掩盖）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "agent-framework"
      ],
      "evidence": "site docs#3;local repro log 2026-08-29",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "Agent 里接 MCP server，连接失败只给你一句 `Cancelled via...`——没有 401、没有权限提示，看起来像\"被取消了\"。你按\"取消\"排查（重试、看超时），方向全错：**真实错误是 HTTP 401 认证失败，只是被错误处理层包装成了取消语义**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：MCP 错误处理缺陷——HTTP auth 401 被包装成 `Cancelled via...`，真实错误名丢失（issue #7699/#7704/#7656，open 2026-08，维护者正在修复为命名真实错误）。",
          "",
          "链路：MCP client 发起 HTTP 请求 → 服务端返回 401 → 客户端错误包装层 → 显示为「Cancelled」。断点在第三步：**错误语义映射缺失**——你看到的是包装后的症状，不是根因。",
          "",
          "推论：看到 Cancelled 不要按取消语义排查；先验证凭据与权限（大多数真实原因就是 401/scope 不足），或用 `curl -i` 直连 MCP HTTP 端点直接看状态码——绕开包装层，拿到原始错误。",
          "",
          "未实测说明：完整链路需要 LLM provider/Azure Foundry，本环境**未实测**（如实标注）；判定依据 = 上游 issue（三家编号同现象）+ 错误处理机制。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：绕开错误包装层，直连 MCP HTTP 端点看真实状态码",
          "curl -i <你的 MCP HTTP 端点>",
          "# 看到 401/403 → 核对凭据与 scope；401 修好后 Cancelled 自然消失",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 直连端点验证 — **必做**",
          "",
          "`curl -i` 看状态码：401/403 = 凭据问题（真实根因）；200 但仍 Cancelled = 继续查下一条。",
          "",
          "### 2. 核对凭据与权限 — **必做**",
          "",
          "API key/头是否带对、scope 是否覆盖该 MCP server 的能力面。",
          "",
          "### 3. 升级到修复版 — **按场景**",
          "",
          "上游修复（命名真实错误）合入并发版后，升级 agent-framework 即恢复可读错误信息。",
          "",
          "### 4. 仍无法定位 — **排查**",
          "",
          "抓 MCP client 侧日志/抓包确认请求确实到达端点；带上原始请求信息跟进 issue。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 连接失败时报出真实的 401/403/命名错误，而不是 Cancelled；",
          "- 凭据修正后 MCP 连接正常建立。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- 企业/Azure Foundry 链路：认证层更多，401 掩盖更容易误导排查；",
          "- 本地 stdio 型 MCP：错误形态不同（无 HTTP 状态码），本条目主要覆盖 HTTP 传输型；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "MCP 连接失败，客户端只显示 \"Cancelled via...\"，看不到真实认证错误（401）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "MCP 错误处理缺陷：HTTP auth 401 被包装成 \"Cancelled via...\"（issue #7699/#7704/#7656，open 2026-08），维护者正在修复为命名真实错误。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 先核对凭据与 scope（真实原因多为 401/权限不足），不要按 Cancelled 语义排查。",
          "2. 升级 agent-framework 到含错误修复的版本（修复合入后）；临时绕开：直接 curl MCP HTTP 端点验证凭据（`curl -i` 看 401/403）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：issue #7699/#7704/#7656（open，2026-08）匿名检索摘要；完整链路需 LLM provider/Azure Foundry，本环境**未实测**（如实标注）。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-framework-3",
        "agent": "https://trove.xin/agent/problems/agent-framework-3"
      }
    },
    {
      "id": "agent-framework-4",
      "title": "agent hooks 装不上/不生效（仓库 setup 只做 git hooks）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "agent-framework"
      ],
      "evidence": "site docs#4;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "按仓库文档跑完 setup，expect 的 agent hooks（如 pre-tool-call）却没有任何动静——工具调用前不触发、日志里也看不到。你重跑 setup 依然如此：因为 **setup 安装的是 git hooks，不是 agent hooks**——两者是不同事件系统里的注册，装完 git hooks 不等于 agent 生命周期事件被挂钩。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：framework 仓库的 setup 任务只安装 git hooks，不提供 agent hooks 的安装途径（deftai/directive issue #3732，2026-08-25）。",
          "",
          "链路：你跑 setup → 注册 git hooks（`.git/hooks/` 下的 commit/checkout 等事件）→ agent 平台的工具调用事件（pre-tool-call 等）无人注册 → 不生效。断点：**两类 hooks 的事件系统不同**——git hooks 挂在 Git 操作上，agent hooks 挂在 agent 平台的生命周期事件上。",
          "",
          "实测说明：本环境**未实测** agent hooks 链路（如实标注）；判定依据 = 官方 setup 的实际行为（只做 git hooks）+ issue #3732 的样本。",
          "",
          "推论：hooks 不生效先问「它注册在谁的事件系统里」：查 `.git/hooks/` 有没有文件只是第一步，agent 侧还要按平台规范（MCP/扩展机制）单独注册配好。",
          ""
        ],
        "快速修复": [
          "",
          "```text",
          "两步分开做：",
          "1) git hooks：仓库 setup 已装（.git/hooks/ 下可见）——保持现状；",
          "2) agent hooks：按你所用 agent 平台的扩展/插件规范单独注册（pre-tool-call 等），配置指向 framework 提供的 hook 入口；注册后重启 agent 会话生效。",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 区分两类 hooks — **必做**",
          "",
          "`ls .git/hooks/` 看到的是 git hooks；agent hooks 不在这个目录——先在概念上分开。",
          "",
          "### 2. 按 agent 平台注册 hooks — **必做**",
          "",
          "查对应平台（如支持 MCP/扩展的 agent）的 hooks 注册文档，把 framework 的 hook 入口配进去。",
          "",
          "### 3. 重启会话验证 — **必做**",
          "",
          "注册后重启 agent 会话，触发一次工具调用，看 pre-tool-call 是否执行（日志/回调输出）。",
          "",
          "### 4. 仍不生效 — **排查**",
          "",
          "核对 hook 可执行权限、入口路径、平台配置格式；带上平台与版本信息跟进 issue。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 工具调用前 hook 实际执行（日志可见）；",
          "- git 侧行为不受影响（原有 git hooks 照常）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- 不同 agent 平台的 hooks 注册机制差异大：按平台文档走，不要假设通用；",
          "- 未实测说明：本环境未挂任何 agent 平台 hooks 链路，如实标注；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "按文档执行 setup 后 agent hooks（如 pre-tool-call 等）没有生效/无法安装。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "framework 仓库的 setup 任务只安装 git hooks，不提供 agent hooks 安装途径（demand 样本 #3732，2026-08-25）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 区分两类 hooks：git hooks（setup 装的）与 agent hooks（需按 agent 平台规范单独注册）。",
          "2. 按对应 agent 平台（如 MCP/扩展机制）文档手工注册 hooks 配置。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：deftai/directive issue #3732（2026-08-25）匿名检索摘要；本环境**未实测**（如实标注）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-framework-4",
        "agent": "https://trove.xin/agent/problems/agent-framework-4"
      }
    },
    {
      "id": "agent-skills-1",
      "title": "官方 CLI 安装失败：GitHub clone 被重置/卡死（国内高频）",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "agent-skills"
      ],
      "evidence": "site docs#1",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "执行 `npx -y skills add addyosmani/agent-skills --yes --global`，卡在 Cloning repository… 后报 `fatal: unable to access ... Connection reset by peer` / Installation failed。",
          "",
          "最坑的是：**CLI 失败时 exit 码仍为 0**，只看命令返回值会误判成功。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "skills CLI 安装流程依赖 `git clone https://github.com/addyosmani/agent-skills.git`（直连）。国内出口到 GitHub 直连不稳定（本市场连通性基线：GitHub 直连慢/被重置），clone 大一点就被重置。",
          "",
          "“安装失败但 exit 码是 0”是 CLI 工具常见的**错误报告缺口**：CLI 的退出码只反映“主流程有没有跑到最后”，不反映“每个子步骤成没成功”——这个 CLI 把 clone 失败当作可恢复的中间步骤，吞掉错误后仍以 0 退出。所以“看 exit 码判断成败”在工具链里不可靠，**判断成败要看最终产物**（命令的目标是“24 个技能目录就位”，那就查目录）。",
          "",
          "这正是“以产物为准”原则：安装类命令的契约是“它承诺的产物存在”，不是“它返回 0”。",
          "",
          "这类 CLI 的另一个特点是**安装过程把网络步骤内嵌**（clone 是流程的一部分），网络一断整个流程就卡/失败——但网络步骤是可以替换的：方式 B（codeload tarball）把“clone 源码仓库”换成“下载压缩包”，产物相同、通道更稳。",
          "",
          "判断路径固定：报错文本 `Connection reset by peer` = 直连通道问题 → 换通道（方式 B）→ 以产物验证（目录就位）而非 exit 码。",
          "",
          "| 检查点 | 说明 |",
          "|---|---|",
          "| 报错文本 | `Connection reset by peer` = 直连被断，不是命令写错 |",
          "| exit 码 | ❌ 失败也是 0，不能当判断依据 |",
          "| 成功判据 | 24 个技能目录就位（`skills/` 下） |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：方式 B（国内推荐，实测可行）——codeload tarball 走 gh-proxy，解压后手工复制",
          "curl -sL https://gh-proxy.com/https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz && cp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills",
          "```",
          "",
          "0.6.7 为当前收录版本，升级时换 tag。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认失败形态 — **必做**",
          "",
          "看到 `Recv failure: Connection reset by peer` 就是直连问题；给 CLI 装代理环境变量（git 走代理）后重试方式 A 也可以。",
          "",
          "### 2. 方式 B：tarball 手工复制 — **必做**",
          "",
          "```bash",
          "curl -sL https://gh-proxy.com/https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz",
          "```",
          "",
          "实测：经 gh-proxy 拉 codeload tarball 277KB 秒级完成。",
          "",
          "### 3. 复制技能目录 — **必做**",
          "",
          "```bash",
          "cp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills",
          "```",
          "",
          "### 4. 判断成功看输出，不是 exit 码 — **必做**",
          "",
          "```bash",
          "ls ~/.claude/skills/agent-skills | head",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "技能目录就位（实测输出形态）：",
          "",
          "```text",
          "ls ~/.claude/skills/agent-skills",
          "→ 24 个子目录 + 24 个 SKILL.md 全部就位",
          "```",
          "",
          "而不是只看命令 exit 码（CLI 失败时也是 0）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 直连 GitHub | 被重置（实测：CLI 下载成功但 clone 被重置） |",
          "| 走 gh-proxy | codeload tarball 277KB 秒级完成（实测） |",
          "| 有代理 | 给 git/CLI 配代理后方式 A 可重试 |",
          "| Windows / macOS | tarball 方式相同，`~/.claude/skills/` 路径对应各平台用户目录 |",
          "| 升级版本 | 换 tag（0.6.7 → 新版本号）重拉 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`npx -y skills add addyosmani/agent-skills --yes --global` 卡在 Cloning repository… 后报 `fatal: unable to access ... Connection reset by peer` / Installation failed。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "skills CLI 安装流程依赖 `git clone https://github.com/addyosmani/agent-skills.git`（直连）；国内出口到 GitHub 直连不稳定（本市场连通性基线：GitHub 直连慢/被重置）。注意：CLI 失败时 exit 码仍为 0，只看命令返回值会误判成功。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 方式 B（国内推荐，实测可行）：codeload tarball 手工复制——`curl -sL https://gh-proxy.com/https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz`，再 `cp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills`（0.6.7 为当前收录版本，升级时换 tag）。",
          "2. 或给 CLI 装代理环境变量（git 走代理）后重试方式 A。",
          "3. 判断成功看输出（24 技能目录就位）而不是 exit 码。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "本站实测记录（2026-08-28）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-skills-1",
        "agent": "https://trove.xin/agent/problems/agent-skills-1"
      }
    },
    {
      "id": "agent-skills-2",
      "title": "安装失败排查：Node 版本 / pnpm / npm 缓存",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "agent-skills"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "skills CLI 或安装过程报 Node 版本不支持、pnpm 相关错误、或 npx 下载卡住/失败。装个技能包，前置环境先卡三道。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 报错 | 根因 |",
          "|---|---|",
          "| Node 版本不支持 | CLI 需要 Node >= 18（本环境 v22.22.1 实测可用） |",
          "| npx 下载卡住/失败 | npm 官方 registry 国内直连不稳 |",
          "| pnpm/缓存报错 | 缓存损坏或权限问题中断 npx 下载 |",
          "",
          "`npx` 是一条“下载即执行”的链：先从 registry 拉包（网络层）→ 解包后核对 engines 声明的 Node 版本（运行时层）→ 再执行 CLI（执行层）。",
          "",
          "三层任何一层断了，用户看到的都是“安装失败”，但报错位置完全不同：Node 版本报错来自 engines 检查，说明网络是通的、卡在运行时；下载卡住/超时发生在拉取层，通常是官方 registry 直连不稳；pnpm/缓存报错发生在解包与写缓存阶段，说明包已下载、但本地状态坏了。",
          "",
          "这就是排查顺序固定的原因：先 `node --version` 排除运行时 → 再用环境变量方式换 registry（只对当前命令生效，不污染全局配置）→ 最后才清缓存。顺序反了会反复撞同一堵墙：比如网络不通时清十次缓存也没用。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：版本检查 + 国内镜像重试",
          "node --version && npm_config_registry=https://registry.npmmirror.com npx -y skills add addyosmani/agent-skills --yes --global",
          "```",
          "",
          "本环境实测 npmmirror PONG 0.32s，npx 下载正常。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 Node 版本 — **必做**",
          "",
          "```bash",
          "node --version",
          "```",
          "",
          "<18 用 nvm 升级到 18+。",
          "",
          "### 2. registry 换国内镜像 — **必做**",
          "",
          "```bash",
          "npm_config_registry=https://registry.npmmirror.com npx -y skills ...",
          "```",
          "",
          "环境变量方式只对当前命令生效，不污染全局配置。",
          "",
          "### 3. 清缓存重试 — **必做**",
          "",
          "```bash",
          "npm cache clean --force",
          "```",
          "",
          "或换隔离 HOME 重试（本市场实测用隔离 HOME /tmp/agsk-home）。"
        ],
        "怎么确认修好了": [
          "",
          "npx 下载与 CLI 执行正常（实测形态）：",
          "",
          "```text",
          "npm_config_registry=https://registry.npmmirror.com npx -y skills ...",
          "→ CLI 正常下载并执行，安装流程走到 clone/复制阶段",
          "```",
          "",
          "`node --version` 输出 ≥18（实测 v22.22.1 可用）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Node <18 | nvm 升级后再装 |",
          "| 官方 registry | 国内直连不稳，npmmirror 实测 PONG 0.32s |",
          "| 缓存损坏 | `npm cache clean --force` 或隔离 HOME |",
          "| 权限问题 | 换用户目录/隔离 HOME 绕开 |",
          "| Windows / macOS | 命令相同 |",
          ""
        ],
        "症状": [
          "",
          "",
          "skills CLI 或安装过程报 Node 版本不支持、pnpm 相关错误、或 npx 下载卡住/失败。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "CLI 需要 Node >= 18（本环境 v22.22.1 实测可用）；npm 官方 registry 国内直连不稳；缓存损坏/权限问题会中断 npx 下载。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 确认版本：`node --version`（<18 用 nvm 升级）。",
          "2. registry 换国内镜像：`npm_config_registry=https://registry.npmmirror.com npx -y skills ...`（本环境实测 npmmirror PONG 0.32s）。",
          "3. 清缓存重试：`npm cache clean --force` 或换隔离 HOME。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28）：npmmirror registry 下 npx 下载 skills CLI 正常；Node v22.22.1 满足要求。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-skills-2",
        "agent": "https://trove.xin/agent/problems/agent-skills-2"
      }
    },
    {
      "id": "agent-skills-3",
      "title": "同名项目污染：pip 装到 ivanzwb / 国外多个同名 repo",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "agent-skills"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "按搜索资料执行 `pip install agent-skills`，结果装到无关同名项目；或 clone 到了 jdrhyne/tristanmanchester/AkshitIreddy/mikeng-io/callstackincubator 等同名仓库，命令、产物全对不上。同名污染把你带沟里了。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- **PyPI** 存在 `ivanzwb` 同名包（CSDN 案例）；",
          "- **GitHub** 存在 5 个以上同名 repo（国外信号）；",
          "- 正确仓库 = `addyosmani/agent-skills`（manifest 签名），**无 pip 分发**，安装路径只有 npx/codeload 两条。",
          "",
          "同名污染的本质是“**名字不是有效身份**”：任何分发渠道（PyPI/GitHub/npm）都只保证“这个名字在这个渠道唯一”，不保证“这个名字只属于一个项目”。所以同一个名字可以在 PyPI、GitHub、npm 上分别指向完全不同的项目——搜索引擎把三者混在一起给你，谁排前面取决于 SEO 而不是身份。",
          "",
          "判断身份只能靠**渠道内的权威锚点**：GitHub 用组织名（addyosmani 是作者本尊，manifest 签名确认）；PyPI 用包名归属（`agent-skills` 在 PyPI 上属于 ivanzwb，而本项目**没有** pip 分发——一个项目“有没有某个渠道的分发”本身就是身份信息）。",
          "",
          "所以安装路径固定：本项目只有 npx/codeload 两条官方路径，任何教程让你 `pip install` 或 clone 其它仓库，都是污染源。",
          "",
          "已装错时的清理也走固定顺序：`pip show`/`pip uninstall` 清 PyPI 同名包 → 检查 `~/.claude/skills/` 下多余目录 → 用官方路径重装。",
          "",
          "| 渠道 | 正确目标 | 污染源 |",
          "|---|---|---|",
          "| PyPI | 无 pip 分发（别 pip install） | ivanzwb 同名包 |",
          "| GitHub | addyosmani/agent-skills | 5+ 同名 repo |",
          "| 安装路径 | `npx skills add` / codeload tarball | 各种\"教程\"的绕行命令 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：认准官方路径重装（npx 或 codeload 二选一）",
          "npx -y skills add addyosmani/agent-skills --yes --global",
          "```",
          "",
          "不要 `pip install agent-skills`；GitHub 认准 `addyosmani` 组织。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 认准仓库与安装路径 — **必做**",
          "",
          "```bash",
          "npx skills add addyosmani/agent-skills",
          "```",
          "",
          "或 codeload：`curl -sL https://gh-proxy.com/https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz`。",
          "",
          "### 2. 检查是否已装错 — **排查**",
          "",
          "```bash",
          "pip show agent-skills 2>/dev/null && pip uninstall agent-skills",
          "```",
          "",
          "如果 pip 里有这个包，就是装到 ivanzwb 了，卸载掉。",
          "",
          "### 3. 清理错误技能目录 — **必做**",
          "",
          "检查 `~/.claude/skills/` 下非本条目的目录（来自同名仓库的复制产物），确认后删除。"
        ],
        "怎么确认修好了": [
          "",
          "正确的技能目录存在（实测形态）：",
          "",
          "```text",
          "ls ~/.claude/skills/agent-skills",
          "→ 24 个子目录，每个含 SKILL.md",
          "```",
          "",
          "`pip list | grep agent-skills` 无输出（不再有 ivanzwb 包）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| PyPI 污染 | `pip uninstall` 卸载同名包（CSDN 案例） |",
          "| GitHub 同名 repo | 认准 addyosmani 组织 + manifest 签名 |",
          "| 教程绕行命令 | 一律回官方路径 npx/codeload |",
          "| Windows / macOS | 目录路径对应各平台用户目录 |",
          ""
        ],
        "症状": [
          "",
          "",
          "按搜索资料执行 `pip install agent-skills` 装到无关同名项目；或 clone 到 jdrhyne/tristanmanchester/AkshitIreddy/mikeng-io/callstackincubator 等同名仓库，命令/产物对不上。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "PyPI 存在 ivanzwb 同名包（CSDN 案例）；GitHub 存在 5 个以上同名 repo（国外信号）；正确仓库 = `addyosmani/agent-skills`（manifest 签名），无 pip 分发，安装路径只有 npx/codeload。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 认准仓库与安装路径：`npx skills add addyosmani/agent-skills` 或 codeload `addyosmani/agent-skills` tag tarball；不要 pip install。",
          "2. 已装错时卸载 pip 包并清理对应技能目录（~/.claude/skills 下非本条目目录）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材引用：CSDN 教程（ivanzwb 同名包案例）+ 国外同名 repo 清单（L32 信号）；正确仓库签名见 manifest（站内文档）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-skills-3",
        "agent": "https://trove.xin/agent/problems/agent-skills-3"
      }
    },
    {
      "id": "agent-skills-4",
      "title": "装完看不到技能 / document-skills 安装问答",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "agent-skills"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "按教程装完（/plugin install 流程或方式 B），在 agent 里看不到技能；或 document-skills 子集安装后没效果。装了半天，白装了。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "技能包本质是**目录复制**（无二进制），\"装完看不见\"基本就三个原因：",
          "",
          "| 原因 | 表现 |",
          "|---|---|",
          "| 装错目录 | 技能在别的路径，agent 扫描不到 |",
          "| 没重启 agent | 技能在启动时扫描，运行中不会刷新 |",
          "| 权限/路径不对 | 复制失败或半成品目录 |",
          "",
          "document-skills 属同一技能集子集，问题同源（网络/权限/版本不兼容是常见失败面，CSDN 安装问答）。",
          "",
          "技能包是“目录即能力”：agent 的技能发现机制是**启动时扫描固定目录**（`~/.claude/skills/` 下每个含 `SKILL.md` 的子目录算一个技能），不是运行时实时刷新——这是“装完看不见”的根因所在。整个链路是：复制目录（写盘）→ agent 启动扫描（读盘）→ 技能列表呈现（运行时内存态）。",
          "",
          "三层任何一层断都表现为“看不见”：目录复制到别处 = 扫描目录里根本没有；没重启 = 内存态还是旧的；权限/半成品目录 = 扫描时读失败被跳过。所以排查顺序固定：先 `ls` 核对目标目录（确认写盘层），再完全退出重启（刷新内存态），最后才怀疑权限/版本——顺序反了会误判“白装了”。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：核对目录 → 重启 agent → 再看",
          "ls ~/.claude/skills/agent-skills && echo \"--- 然后完全退出并重启 agent 会话 ---\"",
          "```",
          "",
          "应看到 24 个目录且每个含 SKILL.md。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 核对技能目录 — **必做**",
          "",
          "```bash",
          "ls ~/.claude/skills/agent-skills",
          "```",
          "",
          "应 24 目录，每个含 SKILL.md（实测复制后形态）。",
          "",
          "### 2. 完全退出并重启 agent 会话 — **必做**",
          "",
          "技能在启动时扫描，运行中不会刷新——完全退出（不是开新窗口）再启动。",
          "",
          "### 3. 权限与版本核对 — **必做**",
          "",
          "```bash",
          "ls -ld ~/.claude/skills/agent-skills && cat ~ | head -1",
          "```",
          "",
          "确认目标目录可写；确认 tag 与收录版本（0.6.7）一致。"
        ],
        "怎么确认修好了": [
          "",
          "目录结构与重启后可见（实测形态）：",
          "",
          "```text",
          "ls ~/.claude/skills/agent-skills",
          "→ 24 个子目录 + 24 个 SKILL.md",
          "```",
          "",
          "重启 agent 后技能列表里能看到对应技能；document-skills 子集同样生效。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 装错目录 | 移到正确路径 `~/.claude/skills/agent-skills` |",
          "| 没重启 | 完全退出并重启 agent 会话 |",
          "| 权限问题 | 确认目标目录可写 |",
          "| 版本问题 | tag 与收录版本 0.6.7 一致 |",
          "| 未实测项 | 真实 Claude Code/Cursor/Codex 环境加载 24 技能、document-skills 子集单独安装、--list 浏览模式、全部技能逐项验证、Windows/macOS |",
          ""
        ],
        "症状": [
          "",
          "",
          "按教程装完（/plugin install 流程或方式 B），在 agent 里看不到技能；或 document-skills 子集安装后无效果。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "技能包本质是目录复制（无二进制）：装错目录、未完全退出重启 agent、或复制时权限/路径不对都会「装完看不见」；document-skills 属同一技能集子集，问题同源（网络/权限/版本不兼容是常见失败面，CSDN 安装问答）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 核对技能目录：`ls ~/.claude/skills/agent-skills`（应 24 目录，每个含 SKILL.md）。",
          "2. 完全退出并重启 agent 会话再试（技能在启动时扫描）。",
          "3. 权限问题：确认目标目录可写；版本问题：确认 tag 与收录版本（0.6.7）一致。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28）：方式 B 复制后目标目录 24 目录 + 24 个 SKILL.md；重启扫描行为为通用机制，未在真实 agent 环境复测——如实标注。",
          ""
        ],
        "未能实测项（如实标注）": [
          "",
          "- 真实 Claude Code/Cursor/Codex 环境加载 24 技能（需对应 IDE/账号）；document-skills 子集单独安装；--list 浏览模式；全部 24 技能逐项验证；Windows/macOS 环境。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-skills-4",
        "agent": "https://trove.xin/agent/problems/agent-skills-4"
      }
    },
    {
      "id": "agent-toolchain-coexist",
      "title": "Agent 工具链全家桶能一起装吗？（17 工具 57 对实测兼容清单）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "coexist-compat",
      "category_label": "组合与兼容",
      "applies_to": [
        "agent-framework",
        "agent-governance-toolkit",
        "agent-skills",
        "agentshield",
        "deepcode",
        "code-review-graph",
        "claude-code-router",
        "logfire",
        "moltis",
        "oh-my-claudecode",
        "herdr",
        "skillspector",
        "oh-my-pi",
        "hoop",
        "swe-rex",
        "superpowers",
        "claude-mem"
      ],
      "evidence": "site docs",
      "source": "本站实测沉淀（2026-08-27）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "想搭一套完整的 Agent 工具链（编排 + 技能 + 记忆 + 网关 + 安全 + 审查 + 可观测），不确定这些工具能不能一起装、会不会互相踩配置。**结论：17 个工具两两共 57 对实测全部兼容。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Agent 工具链 17 个工具两两共 **57 对实测全部兼容**：",
          "",
          "| 类型 | 对数 | 说明 |",
          "|---|---|---|",
          "| 无共享写面 | 51 对 | 各自独立 CLI/目录，天然互不干扰 |",
          "| 共享 `~/.claude.json` 写面 | 6 对 | agt/deepcode/code-review-graph/claude-mem 相关对，写不同 key/段，实测共存无覆盖 |",
          "",
          "冲突矩阵 210 对全量面声明中，本清单只含**已实测 verified** 部分；其余推断态（possible-conflict 17 对）待实测后再收录——未测不标。",
          "",
          "多工具能否共存，判断标准是「写面是否重叠」：每个工具的写面 = 它启动/运行时独占读写的文件或目录（配置文件、状态目录、缓存）。无共享写面的工具对天然共存（互不感知）；共享同一写面（如 `~/.claude.json`）时，只要各写各的 key/段且格式是「按 key 合并」的（JSON/ini 类），共存仍安全——冲突只发生在「同一位置以不同语义写入」或「整体覆盖」时。",
          "",
          "确定性推论：判断两个工具是否冲突，先查它们的写面清单（配置路径、状态目录）再比对；实测 57 对全兼容 = 这 17 个工具的写面两两不冲突；「未测不标」不是托词——推断态（possible-conflict）的 17 对必须实测后才转 verified，因为写面分析只能排除明显冲突，不能证明无冲突。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：冲突预检先跑（看写面/端口面声明）",
          "站内冲突预检说明",
          "```",
          "",
          "按需安装，无强制顺序。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 冲突预检 — **必做**",
          "",
          "```bash",
          "站内冲突预检说明",
          "```",
          "",
          "先看写面/端口面声明；或查 `站内文档`。",
          "",
          "### 2. 按需安装 + 逐工具冒烟 — **必做**",
          "",
          "装完逐工具跑官方 CLI 版本命令（exit 0）；同装技能类（agent-skills/superpowers）后核对 `~/.claude/skills/` 目录清单。",
          "",
          "### 3. 组合场景参考组合包 — **必做**",
          "",
          "组合包 001-003（agt+crg 混用 / crg+deepcode 同 venv / claude-mem+deepcode 跨生态），均实测通过。",
          "",
          "### 4. 升级后重跑冒烟 — **必做**",
          "",
          "升级大版本后重跑冒烟；遇到新冲突走问题空间提交求助卡，官方重测后收录。"
        ],
        "怎么确认修好了": [
          "",
          "逐工具冒烟全绿（预期形态）：",
          "",
          "```text",
          "各工具官方 CLI 版本命令 → 全部 exit 0",
          "ls ~/.claude/skills/ → 技能目录清单齐全",
          "```",
          "",
          "组合包场景按 001-003 复现通过（实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 57 对 verified | 全部带 tested_at（2026-08-27：1 对；2026-08-28：56 对） |",
          "| 覆盖 17 工具 | agent-framework(16)、agt(15)、agent-skills(12)、deepcode(9)、code-review-graph(8) 等 |",
          "| 实测方法 | coexist-test.sh 隔离 HOME + venv，双冒烟 exit 0 |",
          "| 未实测部分 | possible-conflict 17 对不在承诺范围内，未测不标 |",
          ""
        ],
        "症状": [
          "",
          "想搭一套完整的 Agent 工具链（编排 + 技能 + 记忆 + 网关 + 安全 + 审查 + 可观测），不确定这些工具能不能一起装、会不会互相踩配置。",
          ""
        ],
        "原因": [
          "",
          "Agent 工具链 17 个工具两两共 57 对实测全部兼容：51 对无共享写面（各自独立 CLI/目录，天然互不干扰）；6 对共享 `~/.claude.json` 写面（agt/deepcode/code-review-graph/claude-mem 相关对）但写不同 key/段，实测共存无覆盖。冲突矩阵 210 对全量面声明中，本清单只含已实测 verified 部分；其余推断态（possible-conflict 17 对）待实测后再收录。",
          ""
        ],
        "解法": [
          "",
          "1. 按需安装，无强制顺序；冲突预检用 `站内冲突预检说明`（先看写面/端口面声明）或查 `站内文档`。",
          "2. 装完逐工具冒烟（官方 CLI 版本命令 exit 0）；同装技能类（agent-skills/superpowers）后核对 `~/.claude/skills/` 目录清单。",
          "3. 组合场景参考组合包 001-003（agt+crg 混用 / crg+deepcode 同 venv / claude-mem+deepcode 跨生态），均实测通过。",
          "4. 升级大版本后重跑冒烟；遇到新冲突走问题空间提交求助卡，官方重测后收录。",
          ""
        ],
        "验证证据": [
          "",
          "- 57 对 verified 全部带 tested_at（2026-08-27：1 对；2026-08-28：56 对），明细见 站内文档。",
          "- 覆盖 17 工具：agent-framework(16 对)、agt(15)、agent-skills(12)、deepcode(9)、code-review-graph(8)、agentshield(8)、swe-rex(7)、logfire(6)、herdr(6)、claude-mem(6)、superpowers(4)、claude-code-router(4)、oh-my-pi(4)、omc(3)、moltis(2)、skillspector(2)、hoop(2)。",
          "本站实测记录",
          "- 未实测部分（possible-conflict 17 对）不在本条目承诺范围内，保持「未测不标」。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/agent-toolchain-coexist",
        "agent": "https://trove.xin/agent/problems/agent-toolchain-coexist"
      }
    },
    {
      "id": "ai-coding-fatigue-consensus",
      "title": "用 AI 编程越用越累？你的问题不是模型，是把「生成」和「验证」搞反了",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-08-30",
      "sections": {
        "症状": [
          "",
          "我交过最贵的一次「学费」，是一个半小时的 bug 定位。",
          "",
          "当时一个诡异的偶发失败，我懒得换模型，让一个便宜的模型去查。它很勤快：翻日志、查调用链、列可能原因，每次都说「可能是这里」，我每次都得停下来验证它说的对不对。二十分钟一轮，三轮下来，我已经被它带偏了三次。最后我换了个顶配模型，它把同样的上下文重读一遍，两分钟给出定位：「第三层缓存没做失效，命中旧值。」",
          "",
          "问题在于：**定位是验证型的活，我让便宜的模型干；修复才是生成型的活，便宜模型反而够用。我整个用反了。**时间、钱、耐心，三样全亏。",
          "",
          "后来我反复琢磨这件事，发现这不是一次失误，是大多数刚把 AI 编程用起来的人都会踩的同一个坑。我见过同事把 AI 当搜索引擎用：把报错贴进去，AI 解释了，复制官方文档的做法，跑不通；再问，再跑不通。一上午过去，他得出结论：「AI 编程都是吹的。",
          "",
          "」我也见过反过来的：一个朋友用 AI 写代码，写完基本不 review 直接提交，直到某次 AI 把「删除」写成了「覆盖」，而他的测试断言只查了状态码，测试还绿着。",
          "",
          "如果你也有这种感觉——AI 好像让一切变快了，但我的时间一点没省，甚至更累了——这篇是写给同类人的。它不是工具教程，是几个在真实项目里把 AI 编程用起来的人，用各自踩过的坑换来的共识：**你的问题不是模型不够好，是节奏错了。**",
          ""
        ],
        "原因": [
          "",
          "先立一条第一性原理：**生成便宜，验证贵**。",
          "",
          "AI 生成一行代码，几毫秒、几分钱；验证这行代码对不对——读进去、理解上下文、判断边界、想清楚它会怎么坏——花的是你这个人最贵的资源：注意力和时间。所以 AI 编程的总账是这样算的：",
          "",
          "> 总成本 = 生成成本（越来越便宜）+ 验证成本（你的时间，一分没少）",
          "",
          "谁承担验证，谁就是成本中心。把验证推给「看起来挺对」，省下的每一秒，都会在以后的 bug 里加倍还回来。",
          "",
          "顺着这条原理，几乎所有「越用越累」的现象都能解释：",
          "",
          "- **顺序反了。**正确节奏是「我先想 → AI 跑 → 我验收」。大多数人实际是「AI 先跑 → 我再看」：AI 已经生成了几千行，你才开始判断这是不是你要的东西——等于把最贵的验证工作，放在最坏的时间点做。",
          "- **模型用反了。**验证型任务（定位 bug、审查、把关）用了便宜模型，生成型任务（按需求写、批量改）用了贵模型。成本错配：便宜模型看不清，你反复替它验证；贵模型在「生成」这种不值得花钱的地方烧钱。",
          "- **上下文不是免费的。**每往 prompt 里加一句话，都是此后每一轮对话都要支付的成本；常驻上下文越长，缓存命中率越低。有人把 2 万行日志直接糊进去问「为什么报错」，等于一上来就把窗口打爆，然后抱怨模型「记不住」。",
          "- **半吊子水平。**Prompt 写两句就完事，业务规则讲不清楚，测试和可观测性基建懒得搞。AI 时灵时不灵，今天跑通明天就废——这真不能怪 AI，是地基没打。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 1：先给任务分类——生成型用便宜模型，验证型用最贵的脑子",
          "",
          "现象：我一开始也把贵模型当「周末才舍得开的好车」供着，平时让便宜模型干活。结果便宜模型看什么都「挺好」，等于没验。",
          "",
          "做法：把活分成两类：**生成型**——按需求写代码、批量改造、出素材，中档模型是主力：快、便宜、错了重来的成本低；**验证型**——代码审查、质检、定位疑难 bug、创作把关，上最高档，别省。",
          "",
          "为什么：改 bug 是最容易用反的地方，因为它其实是两个动作：**定位是验证型，修复是生成型**。定位要全库找线索，读的比写的多，得用贵的；定位清楚之后的修复常常就改三行，便宜模型绰绰有余。大多数人正好反着来：便宜模型定位半天定不着，火了，换贵模型从头再排一遍——两头的钱都花了，跟我的那次「学费」一模一样。",
          "",
          "### 经验 2：让「我先想，让 AI 跑，我检查」成为肌肉记忆",
          "",
          "现象：面试 AI Coding 相关的岗位时，我最怕听到候选人把 AI 聊得像新闻联播：上下文窗口、推理模型、多模态、MCP，一个词接一个词。一问「你自己平时怎么用 AI 写代码、怎么判断它写得对不对」，马上开始虚。",
          "",
          "做法：判断一个人是不是真的会用 AI，就看一句话：**他的回答里是否自然出现「我先想，让 AI 跑，我检查/判断」的节奏**。同样觉得模型慢：「模型慢了？换个快的」是观众；「慢在读上下文还是推理还是输出？prefill 慢就精简上下文，推理慢就关推理，decode 慢就限制输出」才是驾驶员。",
          "",
          "为什么：一个人懂不懂 AI，不是看他能说出多少模型名字，而是看他能不能把 AI 装进自己的工程流程。AI 是执行者，不是决策者；你把决策权交出去的那一刻，效率就归它管了——而它没有你的上下文。",
          "",
          "### 经验 3：验证要有「断什么」，不是「跑没跑通」",
          "",
          "现象：用 AI 写测试时，它默认输出的断言是这样的：`assert resp.status_code == 200`。接口通了，测试绿了——但业务对吗？订单金额算对没？优惠叠加的顺序对没？积分发对没？全都没断。",
          "",
          "做法：真正要断的是业务结果，不是接口状态。比如促销「满 300 减 50、会员 8 折、叠加 20 元券」：要断的是 `expected = (300 - 50) * 0.8 - 20`，是折扣顺序 `fullcut_discount_coupon`，是风控规则触发了没有。这些断言的逻辑，AI 写不出来——因为它不知道你的业务规则，得你讲清楚。",
          "",
          "为什么：「断什么」从来不是语法问题，是业务理解问题。这一层连人都常常说不清楚，AI 当然只能瞎猜。所以验证型任务里，最值钱的部分不是让 AI 写断言，是**你把业务规则讲清楚**。你讲得越清楚，AI 的验证才越有用；你懒得讲，AI 就替你「猜」，然后你替它兜底。",
          "",
          "### 经验 4：上下文经济学——逐句问「删掉它，行为会变吗」",
          "",
          "现象：我最早给 Agent 写配置的时候，恨不得把注意事项全写进去，结果常驻上下文越来越长，模型反而「变笨」了：缓存命中率下降，每次对话更慢更贵，还经常被废话带偏。",
          "",
          "做法：对常驻 prompt 逐句做一个测试：**如果删掉这句话，Agent 的行为会改变吗？不变，就立刻删**。「Be thorough and careful」这类鼓励语删掉；「This is crucial because…」这类废话前缀删掉；重复的步骤说明删掉。另外用正面表述：「编辑前先读文件」，而不是「别忘了读文件」——否定式反而会让被禁止的行为对模型变得更「可及」。",
          "",
          "为什么：每往 prompt 里加一句话，都是此后每一轮对话都要支付的 Token 成本；删掉一行废话，收益在整个会话周期里持续兑现。同样的道理适用于技能：一个技能解决不了别的技能解决不了的问题，就删。技能库不是收藏夹，每个技能都是常驻成本。",
          "",
          "### 经验 5：把裁决沉淀下来，别让 AI 每轮重复踩同一个坑",
          "",
          "现象：我一度很烦 AI 审查代码：同一个低优先级问题，它每轮都报成 high，我要么忽略（审查失效），要么每次都解释一遍（浪费 Token）。直到我意识到：问题不在它，在**我没有给它一份「裁决基线」**。",
          "",
          "做法：把审查口径沉淀成一个 git 跟踪的文件：比如「内部工具的缺失鉴权降级为 low」「生成文件权重 0、业务逻辑权重 1」，让 AI 命中这些模式时必须按条目降级并引用。再配合校准问题：每条 finding 先过四问——能构造反证吗？换个审稿人会通过吗？是缺陷还是个人偏好？证据在影响范围内吗？",
          "",
          "为什么：AI 没有长期记忆，它每一次都在「猜」你的标准。你把裁决写下来，它就不猜了；你不写，它就按训练数据里的通用标准来，而通用标准跟你的项目大概率对不上。**沉淀是让 AI 从「每次都要你教」变成「教一次就记住」的唯一办法**——这条经验对 Agent 配置、代码审查、测试断言全都适用。",
          "",
          "### 怎么开始用（落地清单）",
          "",
          "1. 今天就做一次任务分类：把手头常用的 AI 用法列出来，标成「生成型/验证型」，验证型任务换最好的模型；",
          "2. 下次让 AI 干活前，先花 30 秒写下「我要它做什么、验收标准是什么」，再让它跑；",
          "3. 检查你的测试断言：有没有只断「跑通了」没断「结果对」的？把业务规则补进去；",
          "4. 给你的常驻 prompt 做一次减法：逐句问「删掉它行为会变吗」，删掉所有不变的话；",
          "5. 把最近一次「AI 反复踩同一个坑」的裁决写下来，让 AI 下次直接引用。",
          "",
          "收尾：最后说点掏心窝的：我写这篇，不是劝你少用 AI——恰恰相反，我用得越来越狠。我只是希望你别像我一样，先花一个半小时的学费才明白：**AI 编程的效率，不取决于模型，取决于你把「想、跑、验」三段节奏的哪一段交给了谁**。想清楚这件事，AI 从「偶尔好用」变成「稳定省时间」，只差一次分类、一次减法、一次沉淀。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ai-coding-fatigue-consensus",
        "agent": "https://trove.xin/agent/problems/ai-coding-fatigue-consensus"
      }
    },
    {
      "id": "ai-coding-honeymoon-consensus",
      "title": "AI Coding 蜜月期之后——代码生成不是瓶颈了，那瓶颈在哪？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（4 篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "我经历过一次印象非常深的「蜜月期幻灭」。",
          "",
          "一个新功能需求——给数据系统的一组算子上补一种跨时间分区的计算——我把它交给了当时最新一代模型驱动的 Agent。结果相当惊人：不到两个工作日，从分析现有代码、理解需求、设计方案，到写功能、补测试、生成文档，它全走完了，初步测试也没发现明显问题。按传统流程，这种需求涉及底层执行逻辑和多个算子，人工开发往往要数周。我当时的第一反应不是怀疑，是兴奋：AI Coding 的效率确实被严重低估了。",
          "",
          "然后测试范围扩大，问题开始成批出现。最折磨人的是：它不是「代码写错了」。需求理解基本正确，算法设计也没明显问题。真正的问题是——**它没有掌握几十万行代码背后那些分散的技术约束**：哪些分区方式有特殊限制、哪些数据类型还没支持、哪些能力只存在于 SQL 层而函数式接口没实现、哪些历史遗留限制会影响新功能。这些约束分散在代码、文档和老人的脑子里，从没被集中写下来，Agent 当然不知道。",
          "",
          "最终测试阶段炸出几个大类、几十个预期外问题，功能「两天写完」，测试和返工花了两周。",
          "",
          "如果你也在经历类似的事——用 AI 写代码明显变快了，但项目没有变快：合入 PR 变快了，Review 却开始排队；需求实现了，上线前测试暴露的问题比以前更多；你总觉得「得盯着它」，结果盯它比写它还累——这篇是写给同类人的。它不是某个工具的使用教程，是几个把 AI Coding 真正放进生产环境的人，用各自的「蜜月期幻灭」换来的共识：**代码生成已经不是瓶颈了，瓶颈转移了。**",
          ""
        ],
        "原因": [
          "",
          "先回答那个反直觉的问题：AI 生成代码的速度已经快到夸张（有公司新产生代码的 75% 由 AI 生成，有团队几乎 99% 的输出 token 来自 Coding Agent），为什么你的交付速度没跟上？",
          "",
          "**第一，交付链路上只有「编码」这一格提速了。** 一个需求从提出到上线，远不止写代码：需求分析、方案设计、开发、Review、测试、联调、上线。AI 只优化了其中「开发」一格——甚至只优化了「写代码」这个动作。Review 还是人看，测试还是人跑，需求还是人理。瓶颈没有消失，只是从「写不出来」转移到了「验证不过来」「约束说不清」。",
          "",
          "**第二，隐性约束是 Agent 的盲区。** 一个成熟系统的代码库里，散落着大量「没写成文档的规矩」：哪块逻辑有历史包袱、哪个接口只能这么调、哪种写法会触发已知的性能陷阱。人靠经验、靠问、靠踩过坑记住它们；Agent 没有这些——它只有你喂给它的上下文。**需求它可以理解对，但「你们公司的历史」它无从知道。** 这不是模型不够聪明，是知识没有被喂到它嘴边。",
          "",
          "**第三，上下文是预算，不是窗口。** 窗口再大，常驻内容越多，缓存命中率越低、每轮成本越高。很多人把整个仓库、两万行日志直接糊进去，窗口是没爆，但「该记住的没记住，该丢掉的没丢掉」——Agent 被噪声淹没，输出质量反而下降。",
          "",
          "**第四，生成越快，校验越贵。** 以前代码写得慢，错误也产生得慢；现在错误是流水线式的。没有配套的验证机制（测试怎么断、审查看什么、降级怎么走），AI 的高速度只会把错误更快地送进生产。",
          "",
          "所以 AI Coding 的提效公式不是「AI 写代码快 = 项目快」，而是：",
          "",
          "> 项目速度 = 生成速度（AI 已解决）+ 约束清晰度（没解决）+ 验证效率（没解决）+ 人的判断（越来越贵）",
          "",
          "下面每一条，都是把「生成快」真正变成「项目快」的做法——它们不是一家之言，是几套独立实践反复验证的共识。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：瓶颈不在生成，在「验证 + 约束 + 环境」",
          "",
          "**别再把 AI Coding 当打字机用——生成已免费，真正决定速度的是：①约束清不清楚（AI 知不知道你公司的规矩），②验证快不快（测试/审查/归因），③环境齐不齐（知识、权限、降级链）。** 没时间？只看这条。经验 1-5 讲具体怎么做。",
          "",
          "### 经验 1：先给交付链路测速——AI 只优化了「编码」一格",
          "",
          "现象：团队用上 AI Coding 后，我一度以为效率起飞，直到看了真实数据：单功能的编码时间确实从几天压到几小时，但需求澄清、Review、测试修复的时间一条没动。整体交付周期只缩短了一点点，瓶颈变成了「代码进来太快，人看不过来」。",
          "",
          "做法：①把交付链路拆成格子：需求分析 → 方案设计 → 编码 → Review → 测试 → 联调 → 上线；②给每个格子记一个真实耗时（别估，翻两周的项目记录）；③标出 AI 只优化了哪一格，剩下最长的格子就是你的真瓶颈；④按瓶颈排投入：Review 慢就拆小 PR + 审查清单，测试慢就把测试做成 AI 也跑的护栏，需求糊就先写方案再动手。",
          "",
          "为什么：人天生会高估「自己正在做的事」的改进。测速表是打破幻觉的最低成本手段——它把「AI 让我变快了」这种模糊感觉，变成「编码 3h→0.5h，Review 还是 4h」这种可行动的事实。瓶颈从来不是「你觉得」的那个，是数据说的那个。",
          "",
          "### 经验 2：把隐性约束显性化——AI 不知道的规矩，写成它开工前必读",
          "",
          "现象：那次跨时间分区功能翻车后我复盘：Agent 对需求的理解是对的，但它不知道「哪些能力只在 SQL 层、函数式接口还没实现」「哪种分区方式有历史限制」——这些约束在代码里、在老人脑子里，就是不在 Agent 能读到的地方。同样的事也发生在团队里：AI 生成的代码风格统一、结构漂亮，但一碰核心模块就踩进历史坑。",
          "",
          "做法：①建一份「约束清单」文档：这个系统有哪些已知限制、哪些能力只存在于某层、哪些写法是雷区、哪些历史包袱不能动——每次 AI 开工前强制它先读；②把「踩过坑的判据」沉淀成规范：哪条判据是被驳回后改的、哪个正则踩过坑、哪次额度撞了，写下来，下一篇/下次任务它自动照办；③知识要让它「能检索」，而不是「背下来」：文档、Skill、示例库，放 Agent 能查的地方，比塞进 prompt 更持久；",
          "",
          "④新人能踩的坑，Agent 一样会踩——约束清单同时就是新人手册。",
          "",
          "为什么：Agent 不会「知道」它没被喂过的东西，但它有个优点：你只要写一次，它每次都照办，不会像人一样忘。**隐性约束显性化的过程，等于把团队的部落知识翻译成机器可读的规范**——这件事本身是复利：写一次，整个团队和所有 Agent 长期受益。",
          "",
          "### 经验 3：上下文是预算，不是窗口——给 Agent 做减法",
          "",
          "现象：给 Agent 配 prompt 的时候，我一开始是「能加就加」：鼓励它、解释背景、重复步骤。直到我逐句做了一次测试——「删掉这句话，行为会变吗？」——才发现删掉四分之一后行为毫无变化。常驻内容每多一句，都是此后每一轮对话都要支付的成本；窗口大不等于可以浪费。",
          "",
          "做法：①逐句测试：任何一句 prompt，删掉它行为不变就删——典型可删的是「Be thorough」「This is crucial」这类鼓励话和废话前缀；②正面表述优先：写「编辑前先读文件」，不写「别忘了读文件」——否定式反而让被禁止的行为更「可及」；③技能一技能一使命：功能重叠的删、纯包装器的删、能内嵌成格式规则的删——技能列表本身每次都要注入；",
          "",
          "④保护该保护的：配置、规则文件加入保护列表，别让自动压缩把「规矩本身」裁了；",
          "",
          "⑤按百分比设压缩阈值，别按绝对值——换模型换窗口不用重新调参。",
          "",
          "为什么：上下文经济学的核心是：**每个 Token 都是常驻成本，删掉一行废话，收益在整个会话周期里持续兑现。** 你省下的不是一次调用的钱，是每次会话都少付一遍的成本。配置工程的减法比加法难，也值钱得多。",
          "",
          "### 经验 4：给 Agent 配「环境」，不是配「嘴」——权限、校验、降级链",
          "",
          "现象：只读 Agent（探索、审查、诊断类）我一开始只给它一条禁令「不要编辑」，结果它该跑的命令还是跑，有一次差点执行了危险操作。后来我把权限模型改成「默认全拒 + 显式白名单」：编辑 deny、任务 deny、bash 只放行 `git status`/`git diff`/`git log`/`rg` 这类只读命令——风险面立刻收敛。",
          "",
          "同样，代码审查我后来用双轴：一条轴对照规范（风格、命名、结构），一条轴对照规约（正确性、边界、安全），两条轴交叉，比单一审查准得多。",
          "",
          "做法：①只读角色权限一律白名单：默认全拒，只放行确需的只读命令；敏感文件（.env 等）双向把关——读取 deny，压缩保护列表里也要有，防止内容被裁进摘要再泄露；②校验机制比规则本身值钱：双轴审查、校准基线、降级链（便宜模型搞不定就升级、执行失败就重新规划）——让系统在不确定时依然收敛；③明确动作边界：`look into X` 不等于 `fix X`，前者只查不改；",
          "",
          "两个执行 Agent 绝不重叠写同一个文件；",
          "",
          "④基础设施是杠杆不是替代品：数据库、计算引擎、中间件这些经过生产验证的确定性能力，AI 该做的是理解意图、选择和组合它们，而不是「重新实现」。",
          "",
          "为什么：AI 写代码的能力越强，它拥有的权限就越该被当回事。**给 Agent 配环境（权限、知识、校验、降级），比给它写更多话术重要得多**——话术提高单次质量，环境决定长期上限。让 AI 承担「实现」，让工程体系承担「约束」，让基础设施承载「确定性」，你才腾得出手去做判断。",
          "",
          "### 经验 5：人往「判断」上走——问题定义、验证、责任兜底",
          "",
          "现象：面试 AI Coding 相关岗位时，我最怕听到候选人把 AI 聊得像新闻联播：DeepSeek、Agent、上下文窗口、多模态、MCP，一个词接一个词。一问「你自己怎么判断 AI 写得对不对」，马上开始虚。判断一个人是不是真的会用 AI，就看他的回答里是否自然出现「我先想，让 AI 跑，我检查/判断」的节奏。",
          "",
          "做法：①把「写出来」的时间让给「定义清楚」：需求、边界、验收标准写清楚再开工，比让 AI 猜更省总账；②验证优先：不信任 AI 的「看起来对」，信单测、日志、审查、回归——AI 会放大能力，也会放大问题：本来会拆问题的人用 AI 更快，没有判断力的人用 AI 只会更快地把错误提交上去；",
          "",
          "③归因而不是换模型：觉得慢，先拆是读得慢（精简上下文）、想得慢（关推理）、写得慢（限制输出）还是模型本身慢（任务分级）；",
          "",
          "④把「责任不可剥夺」焊死在流程里：「AI 告诉我这么做的」不是借口，上线决策、风险兜底永远是人签字。",
          "",
          "为什么：当代码本身在变便宜，人的价值不会消失，只会转移：**判断、架构、经验、责任越来越贵。** 面试官问「AI 会不会替代你」，不是在考套话，是在看你能不能说出「AI 会替代低价值执行动作，但不会替代问题定义、方案取舍、责任兜底和上线决策」。未来的竞争，不属于写代码最快的 AI，而属于工程环境更好、约束更清晰、判断更贵的人。",
          ""
        ],
        "怎么迁移到你的场景": [
          "",
          "落地不需要等「团队规范」，从明天就能开始：",
          "",
          "1. **画一张交付测速表**：两周的项目记录翻一遍，把需求/编码/Review/测试/上线五格的耗时列出来。哪个格子最长，AI 帮不了你的就是它——先动它。",
          "2. **建一份「约束清单」**：打开你的项目，写下五条「新人和 AI 一定会踩、但没写在任何文档里」的规矩，放到 AI 开工前必读的位置。以后每踩一个坑，往清单里补一条。",
          "3. **给 Agent 做一次减法**：花半小时逐句测 prompt，删掉删了行为不变的句子；技能去重；只读任务换成白名单权限。删完你会明显感觉到输出「干净」了。",
          "4. **把验收标准从「跑通」升级成「断什么」**：AI 写的东西，至少配一条能证明业务对的断言，再合入。",
          "",
          "最后一句话：AI Coding 的蜜月期都会结束，区别只在于——有人把「生成快」当成了终点，有人把它当成了起点，然后去修「验证、约束、环境」这三块真正卡脖子的地方。后者才是 AI 时代把代码变便宜之后，依然值钱的人。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ai-coding-honeymoon-consensus",
        "agent": "https://trove.xin/agent/problems/ai-coding-honeymoon-consensus"
      }
    },
    {
      "id": "apt-404-broken",
      "title": "apt-get update 报 404 Not Found / 源失效（Err:1 ... 404）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "apt"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/1BAH/fenrir/issues/10",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`apt-get update` / `apt-get install` 报源失效：`Err:2 https://deb.debian.org/debian nonexistent-suite Release 404 Not Found`，最后 `E: The repository ... does not have a Release file.` 整个命令退出码 100。",
          "",
          "你什么都没动，为什么突然不行了？多半是**某个源挂了**——`apt-get update` 是\"取清单\"阶段，任何一个源失败都会让整个命令退出 100，即使其它源正常。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "apt 的源指向的仓库不再能提供 `dists/<suite>/Release` 文件时，就报 404 + 无 Release 文件：",
          "",
          "| 场景 | 说明 |",
          "|---|---|",
          "| 源地址本身失效 | 项目不再维护 deb 仓库、域名过期、路径变更 |",
          "| suite 名拼错/不存在 | `bookworm` 写成 `bookworm2`，或指向未发布/已归档的 suite |",
          "| 照旧教程配源 | 发布后仓库被删——\"照教程配源\"最常见的中招场景 |",
          "| 镜像站部分同步 | `main` 有、`universe` 缺失，或拉取中断留半成品 |",
          "",
          "apt 的寻址模型是「源（source entry）→ suite → 组件 → Release/索引文件」的路径拼接：`deb http://host/dists/<suite>/<component>` 对应的就是 `dists/<suite>/Release`（和 `Packages`/`Sources` 索引）。",
          "",
          "404 + 「无 Release 文件」= 该路径上服务器不再提供文件——不是「网络不好」，是「寻址目标不存在」。镜像站部分同步、suite 名拼错、仓库下线都落在同一层。",
          "",
          "确定性推论：报错里的 URL 就是 apt 实际请求的路径，先手动 `curl -I` 那个 URL 验证存在性；`apt-get update` 的 404 只影响该源对应组件，其余源照常（所以「部分源坏了」是常态）；",
          "",
          "修法是换源条目（`/etc/apt/sources.list` 或 `/etc/apt/sources.list.d/`），而不是反复 update；判断路径：`curl -I <报错URL>` 404 = 源失效，200 = 检查本地列表缓存/代理。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先定位坏条目（看 Err: 行），再 curl 验证，坏条目删/注释后重新 update",
          "grep -r \"^deb\" /etc/apt/sources.list /etc/apt/sources.list.d/ && curl -sI <URIs>/dists/<suite>/Release",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先定位是哪个源挂了 — **排查**",
          "",
          "完整看 update 输出里的 `Err:` 行（仓库 URL + suite），再核对条目：",
          "",
          "```bash",
          "grep -r \"^deb\" /etc/apt/sources.list /etc/apt/sources.list.d/",
          "```",
          "",
          "（deb822 格式看 `Types:/URIs:/Suites:` 行。）",
          "",
          "### 2. 验证源是否真实可访问 — **必做**",
          "",
          "```bash",
          "curl -sI <URIs>/dists/<suite>/Release",
          "```",
          "",
          "返回 200 = 源没问题；404 = 条目已失效。",
          "",
          "### 3. 删除或注释坏条目 — **必做**",
          "",
          "```bash",
          "rm /etc/apt/sources.list.d/bad.list",
          "```",
          "",
          "或注释掉，重新 `apt-get update`。",
          "",
          "### 4. 换有效源 — **必做**",
          "",
          "Ubuntu 官方 `http://archive.ubuntu.com/ubuntu/`；Debian 用 `https://deb.debian.org/debian/`；国内可换清华/阿里云镜像（路径结构相同，suite 名不变）。**不要用老教程里的源**——先 curl 验证再写入。",
          "",
          "### 5. PPA 停更 / 版本太旧场景 — **必做**",
          "",
          "老 PPA 被 launchpad 清理后 404，删对应 `/etc/apt/sources.list.d/<ppa>.list` 即可；发行版 EOL 后官方源退役，需升级发行版或改用 `old-releases.ubuntu.com`（仅建议临时用）。"
        ],
        "怎么确认修好了": [
          "",
          "update 全绿（实测输出形态）：",
          "",
          "```text",
          "apt-get update",
          "→ 无 Err: 行，exit 0（坏源移除后实测 apt-exit=0）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 本地 404 服务器 | 同款 `Err: ... 404 File not found` + exit 100（实测复现） |",
          "| 镜像过期 | 换清华/阿里云，suite 名不变 |",
          "| PPA 停更 | 删对应 .list |",
          "| EOL 发行版 | 升级或 old-releases（临时） |",
          "| 退出码 100 | 任何一个源失败都会这样，先定位 Err: 行 |",
          ""
        ],
        "症状": [
          "",
          "`apt-get update` / `apt-get install` 报源失效：",
          "",
          "```",
          "Err:2 https://deb.debian.org/debian nonexistent-suite Release",
          "  404  Not Found [IP: 151.101.90.132 443]",
          "E: The repository 'https://deb.debian.org/debian nonexistent-suite Release' does not have a Release file.",
          "```",
          "",
          "特征：update 阶段出现 `Err:N ... 404 Not Found`（或 `File not found`）并最终 `E: The repository ... does not have a Release file`；",
          "apt-get 退出码 100；常见于：第三方自定义 deb 源下线/路径变了、PPA 停更（launchpad 上老 PPA 被清理）、镜像站过期/组件名拼错、",
          "Ubuntu 旧版本官方源退役（如 12.10 时代的 `archive.ubuntu.com` 老路径）、`/etc/apt/sources.list.d/` 里有残留的坏条目。",
          ""
        ],
        "原因": [
          "",
          "apt 的源（sources.list / sources.list.d/*.sources）指向的仓库不再能提供 `dists/<suite>/Release` 文件时，就会报 404 + 无 Release 文件。",
          "可能原因：",
          "- 源地址本身失效（项目不再维护 deb 仓库、域名过期、路径变更）；",
          "- suite 名称拼错或不存在（如把 `bookworm` 写成 `bookworm2`，或指向尚未发布/已归档的 suite）；",
          "- 源来自旧教程，发布后仓库被删（\"照教程配源\"最常见的中招场景）；",
          "- 镜像站只同步了部分组件（`main` 有、`universe` 缺失），或拉取时网络中断留下半成品。",
          "注意：`apt-get update` 是\"取清单\"阶段，任何一个源失败都会让整个命令退出 100，即使其它源正常。",
          ""
        ],
        "解法": [
          "",
          "1. **先定位是哪个源挂了**：完整看 update 输出里的 `Err:` 行（仓库 URL + suite），再 `grep -r \"^deb\" /etc/apt/sources.list /etc/apt/sources.list.d/`（deb822 格式看 `Types:/URIs:/Suites:` 行）核对条目。",
          "2. **验证源是否真实可访问**：`curl -sI <URIs>/dists/<suite>/Release` —— 返回 200 说明源没问题，404 说明条目已失效。",
          "3. **删除或注释坏条目**：把失效的 .list/.sources 移出目录（`rm /etc/apt/sources.list.d/bad.list`）或注释掉，重新 `apt-get update`。",
          "4. **换有效源**：Ubuntu 官方源 `http://archive.ubuntu.com/ubuntu/`（Debian 用 `https://deb.debian.org/debian/`），",
          "   国内可换清华/阿里云镜像（路径结构相同，suite 名不变）；不要用\"老教程里的源\"——先 curl 验证再写入。",
          "5. **PPA 停更场景**：`add-apt-repository` 过的老 PPA 被 launchpad 清理后 404，直接删对应 `/etc/apt/sources.list.d/<ppa>.list` 即可。",
          "6. **版本太旧场景**：发行版 EOL 后官方源退役（404），需升级发行版或改用 `old-releases.ubuntu.com`（仅建议临时用）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，apt 2.x，用 `-o Dir::Etc::sourcelist=...` 隔离临时源，未触碰系统配置）：",
          "  - 坏源①：`deb https://deb.debian.org/debian/ nonexistent-suite main` → update 报 `Err:2 ... nonexistent-suite Release 404 Not Found`，",
          "    `E: does not have a Release file`，apt-exit=100；",
          "  - 坏源②：本地 404 服务器（`python3 -m http.server` 只返回 404）→ 同样报 `Err: ... 404 File not found` + exit 100，",
          "    与真实\"镜像过期/源失效\"完全同款——本地可稳定复现；",
          "  - 修复链路：换成宿主机有效镜像（`archive.ubuntu.com` + 正确 suite）→ update 正常，apt-exit=0。",
          "- 参考案例：[1BAH/fenrir#10](https://github.com/1BAH/fenrir/issues/10)（自定义 deb 源 404 装不上）、[amandeepg/notifyosdconfig#1](https://github.com/amandeepg/notifyosdconfig/issues/1)（老 PPA 404）同款现象。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/apt-404-broken",
        "agent": "https://trove.xin/agent/problems/apt-404-broken"
      }
    },
    {
      "id": "cc-switch-1",
      "title": "dpkg -i 依赖缺失：libwebkit2gtk 等（Linux 安装/启动失败）",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "cc-switch"
      ],
      "evidence": "site docs#1;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`sudo dpkg -i cc-switch.deb` 报依赖错误、包配置不上：`cc-switch depends on libwebkit2gtk-4.1-0; however: Package ... not installed`。或安装\"成功\"但双击没反应。原因很简单：**deb 是 Tauri 桌面应用，需要 GUI 库**，而 `dpkg -i` 不自动解决依赖。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "deb 包 Depends 声明：`libayatana-appindicator3-1, libwebkit2gtk-4.1-0, libgtk-3-0`——Tauri 桌面应用运行时需要这些 GUI 库。`dpkg -i` 只装包、不拉依赖，缺一个就整体配置失败。无 GUI 库的服务器/容器环境必踩。",
          "",
          "deb 包的核心元数据是 **Depends 声明**：它写明“这个包运行需要哪些其它包”，但**声明依赖 ≠ 安装依赖**——`dpkg -i` 只负责把 deb 解包、注册、配置，不负责把 Depends 里写的依赖装进来（那是 apt 的工作）。缺依赖时 dpkg 的默认策略是**整体失败**：与其装一个“注册了但跑不起来”的包，不如让配置阶段报错，把半安装状态留给你处理。",
          "",
          "这就是“dpkg 报依赖错误”和“apt 装能成功”的根本区别：apt 在装包之前先解析 Depends 并把依赖装齐。",
          "",
          "Tauri 桌面应用尤其依赖 GUI 库（webkit/gtk），服务器/容器默认没有，所以必踩。三个确定性推论：**装前自查**（`dpkg-deb -I` 读 Depends，先装缺的）；**无 GUI 环境换 AppImage**（自包含运行，不碰系统库）；**装一半不可怕**（`dpkg -r` 清理半安装状态，零残留）。",
          "",
          "> 实测环境：2026-08-28 本市场实测：`dpkg -i` 在无 GUI 库环境完整复现该错误（exit=1）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：apt 自动补依赖并完成配置",
          "sudo apt install -f && sudo dpkg -i cc-switch.deb",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 补依赖 — **必做**",
          "",
          "```bash",
          "sudo apt install -f",
          "```",
          "",
          "或直接让 apt 处理：`sudo apt install ./cc-switch.deb`（apt 会自动拉依赖，推荐）。",
          "",
          "### 2. 无 root / 不想装 GUI 依赖 — **必做**",
          "",
          "用官方 **AppImage** 资产：免安装、自包含运行，不碰系统库。",
          "",
          "### 3. 安装前自查 — **必做**",
          "",
          "```bash",
          "dpkg-deb -I cc-switch.deb | grep Depends",
          "```",
          "",
          "看 Depends 行，先装缺失库再 dpkg -i。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "cc-switch --version && cc-switch",
          "```",
          "",
          "命令可用、GUI 能起即成功。实测：补依赖后配置成功；无 GUI 库时复现依赖错误（exit=1），`dpkg -r` 清理半安装状态后零残留。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 服务器/容器（无 GUI 库） | 必踩依赖坑；补 libwebkit2gtk 等或改用 AppImage |",
          "| 桌面 Linux | `apt install ./cc-switch.deb` 自动拉依赖 |",
          "| 无 root | AppImage 免安装形态 |",
          "| 安装\"成功\"但双击无反应 | 缺运行库，补依赖后重试 |",
          ""
        ],
        "症状": [
          "",
          "`sudo dpkg -i cc-switch.deb` 报依赖错误，包无法配置：",
          "",
          "```",
          "dpkg: dependency problems prevent configuration of cc-switch:",
          " cc-switch depends on libayatana-appindicator3-1; however: Package ... not installed.",
          " cc-switch depends on libwebkit2gtk-4.1-0; however: Package ... not installed.",
          "dpkg: error processing package cc-switch (--install): dependency problems - leaving unconfigured",
          "```",
          "",
          "或安装成功但双击无反应（缺运行库）。",
          ""
        ],
        "原因": [
          "",
          "deb 包 Depends：`libayatana-appindicator3-1, libwebkit2gtk-4.1-0, libgtk-3-0`（Tauri 桌面应用需要 GUI 库）；`dpkg -i` 不自动解决依赖，缺一个就整体配置失败。",
          ""
        ],
        "解法": [
          "",
          "1. 补依赖：`sudo apt install -f`（或 `sudo apt install ./cc-switch.deb`，apt 会自动拉依赖）。",
          "2. 不想装 GUI 依赖/无 root 场景：用官方 AppImage 资产（免安装，自包含运行）。",
          "3. 安装前自查：`dpkg-deb -I cc-switch.deb` 看 Depends 行，先装缺失库。",
          ""
        ],
        "验证证据": [
          "",
          "- 本市场实测 2026-08-28（实测记录（本地留存））：`dpkg -i` 在无 GUI 库环境完整复现该错误（exit=1）；随后 `dpkg -r` 清理半安装状态成功，verify 零残留。",
          "- 依赖清单：libayatana-appindicator3-1 / libwebkit2gtk-4.1-0 / libgtk-3-0（同日志 step 2）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/cc-switch-1",
        "agent": "https://trove.xin/agent/problems/cc-switch-1"
      }
    },
    {
      "id": "cc-switch-2",
      "title": "GitHub Release 下载 TLS 断连 / 超时",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "cc-switch"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "从 GitHub Release 下载 cc-switch 的 deb/AppImage 反复断连（TLS/连接重置），13.5MB 的包都下不完。你以为是网络问题等重试——**国内直连 GitHub release 资产不稳定**是常态，用 gh-proxy 前缀加速最省事。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "GitHub Release 资产实际托管在 `objects.githubusercontent.com`，国内直连不稳定，大文件传输中途断开。GitHub 网页/API 可能能开，但**资产下载**是另一条链路，表现完全不同。",
          "",
          "关键认知是“网页能开 ≠ 下载能通”：GitHub 的网页、API 和 Release 资产分别走不同的域名与 CDN——网页/API 在 `github.com`，而 Release 大文件实际由 `objects.githubusercontent.com` 提供，两者链路在墙内表现完全不同。",
          "",
          "下载是长连接大流量传输，中途断连时 TLS 握手已经成功过，所以表现为“连上了又断”，而不是“连不上”。",
          "",
          "gh-proxy 之所以有效，是因为它把直连换成“国内可通的代理服务器 → 代理服务器与 GitHub 之间走境外链路”，把最脆弱的一段（你到 GitHub 的跨国连接）替换掉。验证下载是否完整也有固定顺序：先 `du -h` 对照资产页标注大小（大小差太多 = 断了一半），再用 `dpkg-deb -I` 确认结构可读——大小对 + 结构可读，才算完整。",
          "",
          "> 实测环境：2026-08-28 本市场实测：直连慢（connectivity 记录）；gh-proxy 下载 13.5MB 成功。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：gh-proxy 前缀加速下载（13.5MB 实测秒级）",
          "curl -L -o cc-switch.deb \"https://gh-proxy.com/https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 用 gh-proxy 前缀 — **必做**",
          "",
          "把资产直链前面加上 `https://gh-proxy.com/`：",
          "",
          "```bash",
          "curl -L -o cc-switch.deb \"https://gh-proxy.com/https://github.com/<owner>/<repo>/releases/download/<tag>/cc-switch_x86_64.deb\"",
          "```",
          "",
          "### 2. 或换网络/开代理错峰重试 — **必做**",
          "",
          "校园网/家宽高峰期直连最差，错峰或代理可缓解。",
          "",
          "### 3. 下载后校验 — **必做**",
          "",
          "```bash",
          "du -h cc-switch.deb",
          "```",
          "",
          "对照资产页标注大小（deb 13.5MB / 二进制 31MB），差太多就是断了一半，重新下。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "dpkg-deb -I cc-switch.deb >/dev/null && echo DEB-OK",
          "```",
          "",
          "deb 结构可读即下载完整（实测：gh-proxy 下载 13.5MB 成功，结构确认 exit=0）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 国内直连 | 资产下载断连高频，gh-proxy 前缀 |",
          "| 校园网/家宽 | 错峰或代理重试 |",
          "| 大资产（31MB 二进制） | 下载后必须校验大小 |",
          "| 有稳定代理 | 直连即可，gh-proxy 仅作备选 |",
          ""
        ],
        "症状": [
          "",
          "从 GitHub Release 下载 deb/AppImage 反复断连（TLS/连接重置），13.5MB 的包下不完。",
          ""
        ],
        "原因": [
          "",
          "国内直连 GitHub release 资产（objects.githubusercontent.com）不稳定；大文件传输中途断开。",
          ""
        ],
        "解法": [
          "",
          "1. gh-proxy 前缀加速：`https://gh-proxy.com/https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>`（2026-08-28 实测秒级）。",
          "2. 或换网络/开代理错峰重试。",
          "3. 下载后校验：`du -h` 对照资产页大小（deb 13.5MB / 二进制 31MB）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本市场实测：直连慢，gh-proxy 下载 13.5MB 成功（结构确认 + 闭环日志 step 1 exit=0）。",
          "- GitHub API 直连可用（资产清单核对，connectivity 记录）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/cc-switch-2",
        "agent": "https://trove.xin/agent/problems/cc-switch-2"
      }
    },
    {
      "id": "cc-switch-3",
      "title": "架构不匹配（香橙派等 ARM 设备）",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "cc-switch"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "ARM 设备（香橙派等）安装 x86_64 包报架构错误，或下载了 arm 包但 GUI 起不来。官方其实覆盖 amd64/arm64 等全平台（19 个资产），问题多半是**下载时没按架构选**，或轻量 ARM 系统缺桌面库。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- 装错架构：`dpkg -i` 对架构不匹配直接拒绝（`wrong architecture`）；",
          "- 选对 arm64 包但 GUI 起不来：部分 ARM 轻量系统（无桌面/精简发行版）缺 GTK 等桌面库，同症状 1 的依赖问题。",
          "",
          "> 实测环境：本市场 amd64 包结构确认（/usr/bin/cc-switch，ELF x86-64，31MB）；ARM 场景为网页案例（CSDN）。",
          "这个报错其实包含**两层独立的检查**，一层一层来：第一层是 dpkg 的架构门禁——deb 包元数据里有 `Architecture` 字段，`dpkg -i` 会拿它和系统的 `dpkg --print-architecture` 比对，不一致直接拒绝（`wrong architecture`），这是 Debian 系防止装错包搞坏系统的设计，报这个错说明你拿错了资产。",
          "",
          "第二层是动态链接依赖——就算架构选对了，GUI 起不来多半是缺 GTK/webkit2gtk 这类桌面库。",
          "",
          "关键区别在于：用 `apt install ./xxx.deb` 会自动解析并拉取依赖，用 `dpkg -i` 裸装则不会。所以\"先查架构 + 用 apt 装\"两步缺一不可。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先查架构再装（示例 arm64）",
          "dpkg --print-architecture && sudo apt install ./cc-switch_arm64.deb",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 选对架构资产 — **必做**",
          "",
          "```bash",
          "uname -m",
          "```",
          "",
          "x86_64 → amd64 资产；aarch64 → arm64 资产。资产页按 Architecture 过滤（x86_64 / arm64 / win / mac）。",
          "",
          "### 2. ARM 桌面系统装 arm64 包 — **必做**",
          "",
          "```bash",
          "sudo apt install ./cc-switch_arm64.deb",
          "```",
          "",
          "apt 自动拉 GUI 依赖（libwebkit2gtk 等）。",
          "",
          "### 3. 无桌面环境 — **必做**",
          "",
          "不建议装 GUI 形态，考虑 CLI 替代方案（如直接编辑 ~/.claude.json 切换）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "cc-switch --version",
          "```",
          "",
          "能输出版本号、GUI 能起即成功（AMD 实测：包结构为 ELF x86-64 31MB，安装验证通过）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 香橙派等 ARM | 选 arm64 资产 + apt 拉依赖 |",
          "| x86_64 桌面 | amd64 资产 |",
          "| 轻量 ARM 无桌面 | GUI 形态不建议，用 CLI 替代 |",
          "| 架构选错 | dpkg 直接报 wrong architecture，重选资产 |",
          ""
        ],
        "症状": [
          "",
          "ARM 设备（香橙派等）安装 x86_64 包报架构错误，或下载 arm 包后 GUI 起不来（GTK 问题）。",
          ""
        ],
        "原因": [
          "",
          "官方 19 个资产覆盖 amd64/arm64 等全平台；装错架构包 dpkg 直接拒绝（`wrong architecture`）；部分 ARM 轻量系统缺桌面库。",
          ""
        ],
        "解法": [
          "",
          "1. 选对架构资产（资产页按 Architecture 过滤：x86_64 / arm64 / win / mac）。",
          "2. ARM 桌面系统装 arm64 包 + 对应 GUI 依赖（同症状 1 的 apt 方式）。",
          "3. 无桌面环境设备不建议装 GUI 形态（考虑 CLI 替代方案）。",
          ""
        ],
        "验证证据": [
          "",
          "- 网页案例：CSDN（香橙派 ARM 架构 + GTK 场景）。",
          "- 本市场实测：amd64 包结构确认（/usr/bin/cc-switch，ELF x86-64，31MB——）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/cc-switch-3",
        "agent": "https://trove.xin/agent/problems/cc-switch-3"
      }
    },
    {
      "id": "cc-switch-4",
      "title": "启动失败 / 白屏排查",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "cc-switch"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "双击/命令行启动 cc-switch 无反应、白屏或闪退。三个常见根因：**运行库缺失**（webkit 等，见依赖条目）、**配置损坏**（`~/.cc-switch/config.json` 与版本不兼容）、**共享配置被改写**（`~/.claude.json` / `~/.codex/config.toml` 段结构不匹配）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "cc-switch 是 Tauri 桌面应用：缺 GUI 库 → 起不来；它管理 `~/.claude.json` 的 provider 段和 `~/.codex/config.toml`，这些共享文件被其它工具改过结构时，它按旧结构解析就白屏/闪退。**终端启动看 stderr** 是最快的定位方式。",
          "",
          "桌面应用启动是“**依赖链 → 配置加载 → 界面渲染**”三段：缺共享库（webkit/gtk 等）时，动态链接阶段直接失败——表现为“双击没反应”（连进程都起不来）；配置损坏或结构不匹配时，进程起来了但解析失败——GUI 框架初始化不出内容，表现为“白屏/闪退”。",
          "",
          "这两个失败点的差异就是定位线索：**完全无反应 = 先查依赖**（`ldd | grep \"not found\"`），**起来但白屏 = 先查配置**。",
          "",
          "还有个隐藏风险面：`~/.claude.json` 这类文件是**多工具共享**的，其它工具按自己的结构改写后，cc-switch 按旧结构解析就崩——所以它只改 provider 段、不碰插件注册段，这是“写面最小化”的设计。终端启动保留 stderr 是最高效的定位方式，因为报错信息直接点名是缺库还是解析失败。",
          "",
          "> 实测环境：无 GUI 库环境二进制不可运行（如实标注）；管理面 = ~/.cc-switch/config.json + ~/.claude.json + ~/.codex/config.toml（二进制字符串审计）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：依赖自查，有 not found 就补",
          "ldd /usr/bin/cc-switch | grep \"not found\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 依赖自查 — **必做**",
          "",
          "```bash",
          "ldd /usr/bin/cc-switch | grep \"not found\"",
          "```",
          "",
          "有输出 → 缺库，`sudo apt install -f` 或按依赖条目补装。",
          "",
          "### 2. 配置备份后重建 — **必做**",
          "",
          "```bash",
          "mv ~/.cc-switch ~/.cc-switch.bak && cc-switch",
          "```",
          "",
          "启动正常后把要的配置从 .bak 迁回。",
          "",
          "### 3. 终端启动看报错 — **排查**",
          "",
          "```bash",
          "cc-switch",
          "```",
          "",
          "保留 stderr 输出，报错会直接指向根因（缺库/配置解析失败）。",
          "",
          "### 4. 检查共享配置段 — **排查**",
          "",
          "```bash",
          "python3 -m json.tool ~/.claude.json >/dev/null && echo JSON-OK",
          "```",
          "",
          "JSON 不合法 → provider 段被写坏；cc-switch 只改 provider 段，不碰插件注册段，恢复备份或手动修正。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "cc-switch --version && cc-switch",
          "```",
          "",
          "版本能输出、GUI 正常打开即成功（依赖补全后不再闪退）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 缺运行库 | ldd 查 not found，apt 补装 |",
          "| 配置损坏 | 备份 ~/.cc-switch 后重建 |",
          "| 共享配置被改写 | 检查 ~/.claude.json JSON 合法性 |",
          "| 无 GUI 环境 | 二进制不可运行，用 CLI 替代方案 |",
          ""
        ],
        "症状": [
          "",
          "双击/命令行启动无反应、白屏、或闪退。",
          ""
        ],
        "原因": [
          "",
          "1. 运行库缺失（webkit 等，见症状 1）；",
          "2. `~/.cc-switch/config.json` 损坏或与版本不兼容；",
          "3. 共享配置被其它工具改写（~/.claude.json / ~/.codex/config.toml 段结构不匹配）。",
          ""
        ],
        "解法": [
          "",
          "1. 依赖自查：`ldd /usr/bin/cc-switch | grep 'not found'`——有输出说明缺库，apt 补装。",
          "2. 配置备份后重试：备份 `~/.cc-switch/` 后删除让其重建。",
          "3. 终端启动看报错：`cc-switch`（保留 stderr 输出）。",
          "4. 共享配置段异常：检查 ~/.claude.json 的 provider 段 JSON 合法性（cc-switch 只改 provider 段，不碰插件注册段）。",
          ""
        ],
        "验证证据": [
          "",
          "- 网页案例：CSDN 启动失败排查场景。",
          "- 本市场实测：无 GUI 库环境二进制不可运行（如实标注，）；管理面 = ~/.cc-switch/config.json + ~/.claude.json + ~/.codex/config.toml（二进制字符串审计，）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/cc-switch-4",
        "agent": "https://trove.xin/agent/problems/cc-switch-4"
      }
    },
    {
      "id": "cc-switch-5",
      "title": "安装来源混淆：npm 同名包 / 其它 cc-switch 项目",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "cc-switch"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "按 \"cc-switch\" 搜索装到了错误的包——npm 上存在同名包，但**不是这个项目**；或装完根本没有 `cc-switch` 命令。认准一点：本项目是 Tauri 桌面应用，分发渠道是 **GitHub Release**（deb/AppImage/rpm/win/mac），**没有 npm 包**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "本项目 = farion1231/cc-switch（Tauri 桌面应用），分发渠道 = GitHub Release 资产；npm 上同名 `cc-switch` 是其它项目。搜索\"cc-switch\"时 npm 结果排前面，容易装错。",
          "",
          "“包名相同”不代表“是同一个东西”——**分发渠道是身份的一部分**：桌面应用（Tauri）的分发渠道是 GitHub Release 资产（deb/AppImage 等），npm 是 JS 包生态，两者机制完全不同。",
          "",
          "`npm install cc-switch` 会装到 npm 上那个同名 JS 包，但它不提供 `cc-switch` 命令——所以“装完 command not found”是判断装错来源的硬信号。",
          "",
          "反过来，装对了也有两个可核对的硬证据：命令面（`cc-switch --version` 能输出）和包元数据（`dpkg-deb -I` 里的 Package/Version/Maintainer 字段）。搜索结果的排序（npm 靠前）只是流量入口，不能当身份证据；判断身份要看“安装命令来自哪个生态 + 装完的命令面/元数据是否匹配”。",
          "",
          "> 实测环境：deb 包元数据 Package: cc-switch / Version: 3.20.0 / Maintainer: Jason Young（2026-08-28 闭环日志）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：认准仓库，从 Release 下载（示例 amd64 deb）",
          "curl -L -o cc-switch.deb \"https://gh-proxy.com/https://github.com/farion1231/cc-switch/releases/latest/download/cc-switch_x86_64.deb\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 认准来源 — **必做**",
          "",
          "- 仓库：`farion1231/cc-switch`；",
          "- 命令/入口：`cc-switch` 或桌面图标；",
          "- 安装一律走 GitHub Release 资产（或本市场收录的 deb 路径），**不要 `npm install cc-switch`**。",
          "",
          "### 2. 装完核对 — **必做**",
          "",
          "```bash",
          "cc-switch --version",
          "```",
          "",
          "报 command not found → 装错来源（npm 同名包没有这个命令），卸载重装 Release 资产。",
          "",
          "### 3. 核对包元数据 — **必做**",
          "",
          "```bash",
          "dpkg-deb -I cc-switch.deb | head -8",
          "```",
          "",
          "Package: cc-switch / Version: 3.20.0 / Maintainer: Jason Young——对得上才是本项目。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "cc-switch --version && which cc-switch",
          "```",
          "",
          "命令存在、版本可输出即成功（实测：Release deb 安装后命令正常）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| npm 搜索 | 同名包是别的项目，别装 |",
          "| GitHub Release | 唯一官方分发渠道 |",
          "| 装完没命令 | 来源装错，换 Release 资产重装 |",
          "| 桌面图标 | 与 `cc-switch` 命令等价，认准图标来源 |",
          ""
        ],
        "症状": [
          "",
          "按 \"cc-switch\" 搜索装到错误的包（npm 上存在同名包，但不是本项目），或装完没有 cc-switch 命令。",
          ""
        ],
        "原因": [
          "",
          "本项目 = farion1231/cc-switch（Tauri 桌面应用），分发渠道 = GitHub Release（deb/AppImage/rpm/win/mac），**没有 npm 包**；npm 同名包是其它项目。",
          ""
        ],
        "解法": [
          "",
          "1. 认准仓库：farion1231/cc-switch；命令/入口 = `cc-switch` 或桌面图标。",
          "2. 安装一律走 GitHub Release 资产（或本市场收录的 deb 路径），不要 `npm install cc-switch`。",
          ""
        ],
        "验证证据": [
          "",
          "- 网页案例：CSDN（npm 包名混淆场景）。",
          "- 本市场实测：deb 包元数据 Package: cc-switch / Version: 3.20.0 / Maintainer: Jason Young（闭环日志 step 2）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/cc-switch-5",
        "agent": "https://trove.xin/agent/problems/cc-switch-5"
      }
    },
    {
      "id": "claude-code-router-1",
      "title": "EBADENGINE：Node 版本不满足 engines",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#1",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`npm install -g @musistudio/claude-code-router` 报 `EBADENGINE`，提示 engine node 不满足要求。不是网络问题、也不是包坏了——**包 engines 要求 Node `20 || >=22`，你系统的 Node 太旧**（18/19/21 都不满足）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "claude-code-router 声明了 engines 字段（Node `20 || >=22`），npm 安装时核对当前 Node 版本，不满足直接报 EBADENGINE 拒绝安装。多 Node 环境（nvm / Windows / WSL 混用）下尤其容易踩：你以为在用新版，实际生效的是旧版。",
          "",
          "> 实测环境：本环境 Node v22.22.1，安装成功（2026-08-28 闭环日志首行 node: v22.22.1）。",
          "`engines` 是包作者声明的**运行环境契约**：npm 安装时读取该字段并和当前 Node 版本比对，不满足就拒绝（EBADENGINE）。这本身是保护——避免装上一个\"能装但跑不动\"的包。多版本环境的坑在另一层：`nvm use 22` 只切换 nvm 自己管理的版本，而 shell 实际执行的是 PATH 里**第一个**命中的 node。",
          "",
          "常见翻车：装了 nvm 的 node 22，但 PATH 里系统 node 18 排在前面，`nvm use` 之后 `node --version` 还是 18。所以排查顺序固定：先 `which node` 看谁在生效，再谈升级。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：nvm 装 22 并切换后重装",
          "nvm install 22 && nvm use 22 && npm install -g @musistudio/claude-code-router",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认当前生效版本 — **必做**",
          "",
          "```bash",
          "node --version && which node",
          "```",
          "",
          "看 `which node` 指向哪——多环境混用时经常 `nvm use 22` 了但 PATH 里还是旧的。",
          "",
          "### 2. 装新版并切换 — **必做**",
          "",
          "```bash",
          "nvm install 22 && nvm use 22",
          "```",
          "",
          "（或 `nvm install 20`，满足 `20 || >=22` 即可。）",
          "",
          "### 3. 重试安装 — **必做**",
          "",
          "```bash",
          "npm install -g @musistudio/claude-code-router",
          "```",
          "",
          "不再报 EBADENGINE 即成功。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ccr --version",
          "```",
          "",
          "能输出版本号即成功（实测：Node v22.22.1 下安装成功，exit=0）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Node 18/19/21 | 不满足 engines，必须升 20 或 22 |",
          "| nvm 多版本 | `nvm use 22` 后确认 `which node` |",
          "| Windows/WSL 混用 | 两侧各装各的，别指望 Windows node 服务 WSL |",
          "| CI | 固定 Node 22 镜像 |",
          ""
        ],
        "症状": [
          "",
          "`npm install -g @musistudio/claude-code-router` 报 EBADENGINE，提示 engine node 不满足要求。",
          ""
        ],
        "原因": [
          "",
          "包 engines 要求 Node `20 || >=22`；系统 Node 过旧（18/19/21 均不满足）。",
          ""
        ],
        "解法": [
          "",
          "1. 用 nvm 装新版并切换：`nvm install 22 && nvm use 22`（或 `nvm install 20`）。",
          "2. 多 Node 环境（nvm/Windows/WSL 混用）先确认生效版本：`node --version`、`which node`。",
          "3. 满足版本后重试安装。",
          ""
        ],
        "验证证据": [
          "",
          "- 网页案例：CSDN（2026-03，EBADENGINE 场景）。",
          "- 本市场实测：本环境 Node v22.22.1，安装成功（首行 node: v22.22.1）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-1",
        "agent": "https://trove.xin/agent/problems/claude-code-router-1"
      }
    },
    {
      "id": "claude-code-router-2",
      "title": "WSL 里 node: not found（npm 来自 Windows 侧）",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "WSL 里执行 `ccr` 或其它 npm 全局命令报 `node: not found`，但同一命令在 Windows 侧正常。原因是**包是 Windows 版安装的**（脚本 shebang 指向 Windows node），或 PATH 里 Windows 路径优先于 WSL 原生路径——两侧的 Node 是两套东西，别混用。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "WSL 和 Windows 各有自己的 node/npm 全局目录。如果在 Windows 侧 `npm install -g`，生成的命令脚本 shebang 指向 Windows 的 node.exe；在 WSL 里执行时找不到对应解释器就报 `node: not found`。反过来，PATH 里 Windows 路径在前时，WSL 里的 `node` 会调到 Windows 版，行为怪异。",
          "",
          "npm 全局命令的真实形态是**一个 shell 脚本**（不是二进制）：`npm install -g` 时，npm 在全局 bin 目录生成一个可执行脚本，脚本第一行 shebang 写死了解释器路径（如 `#!/usr/bin/env node` 或直接指向 node.exe 的绝对路径）。",
          "",
          "安装时写的是**当时那台机器的解释器**：在 Windows 侧安装，shebang 指向 Windows 的 node.exe——WSL 里执行这个脚本时，WSL 环境里没有 node.exe（那是 Windows 的 PE 可执行文件），于是 `node: not found`。",
          "",
          "反过来，WSL 的 PATH 里如果 Windows 路径（/mnt/c/...）排在前面，`node` 会解析到 Windows 版 node.exe——WSL 能执行它（通过 interop），但行为、路径、性能都跟原生版不一样。这就是“两侧是两套东西，别混用”的机制含义。",
          "",
          "修复也因此固定：在 WSL 侧装原生 Node（nvm/apt），把全局包重装到 WSL 侧 npm 的 bin 目录，让 shebang 指向 WSL 的解释器；验证用 `which node` 看解析路径。",
          "",
          "> 实测环境：WSL 原生 Node v22.22.1 + npm 全局安装链路可用（，2026-08-28）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：WSL 内装原生 Node（nvm）并重装全局包",
          "nvm install 22 && nvm use 22 && npm install -g @musistudio/claude-code-router",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. WSL 内装原生 Node — **必做**",
          "",
          "```bash",
          "nvm install 22 && nvm use 22",
          "```",
          "",
          "（或 apt 装 nodejs/npm。）确保 `/usr/bin` 或 nvm 路径在 PATH 中优先于 Windows 路径。",
          "",
          "### 2. 验证生效版本 — **必做**",
          "",
          "```bash",
          "which node && node --version",
          "```",
          "",
          "`which node` 指向 WSL 内路径（/usr/bin 或 ~/.nvm）才是对的。",
          "",
          "### 3. 重装到 WSL 侧全局 — **必做**",
          "",
          "```bash",
          "npm install -g @musistudio/claude-code-router",
          "```",
          "",
          "不要混用两侧 npm 全局目录；旧命令脚本残留的话 `rm $(which ccr)` 清掉 Windows 侧副本。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "which ccr && ccr --version",
          "```",
          "",
          "WSL 内命令可执行即成功（实测：WSL 原生 Node + npm 全局链路可用）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| WSL 执行 Windows 装的包 | node: not found，重装到 WSL 侧 |",
          "| PATH 混用 | 确保 WSL 路径优先 |",
          "| 两侧全局目录 | 各装各的，不共享 |",
          "| CI/容器 | 单侧环境，无此问题 |",
          ""
        ],
        "症状": [
          "",
          "WSL 里执行 `ccr` 或 npm 全局命令报 `node: not found`；同一命令在 Windows 侧正常。",
          ""
        ],
        "原因": [
          "",
          "npm 包是 Windows 版安装的（脚本 shebang 指向 Windows node），或 PATH 中 Windows 路径优先于 WSL 原生路径。",
          ""
        ],
        "解法": [
          "",
          "1. WSL 内装原生 Node（nvm 或 apt），确保 `/usr/bin` 在 PATH 中优先。",
          "2. 重开 shell 验证：`which node`、`node --version`。",
          "3. 不要混用两侧 npm 全局目录（重装到 WSL 侧全局即可）。",
          ""
        ],
        "验证证据": [
          "",
          "- 网页案例：CSDN（2026-03，WSL node: not found 场景）。",
          "- 本市场实测：WSL 原生 Node v22.22.1 + npm 全局安装链路可用。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-2",
        "agent": "https://trove.xin/agent/problems/claude-code-router-2"
      }
    },
    {
      "id": "claude-code-router-3",
      "title": "首次运行 No available models / 网关起不来",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`ccr start` / `ccr serve` 提示 `No available models. Configure at least one provider...`，管理界面没有可用模型。别慌——这是**首次运行的正常现象**：还没配置任何 provider（`~/.claude-code-router/` 下 provider 配置为空），不是故障。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "网关启动时需要一个可用模型列表；配置为空时它如实提示\"没有可用模型\"，并拒绝提供服务。配置好 provider + 模型后重启网关即可。",
          "",
          "网关（gateway）的核心职责是**路由**：把客户端发来的模型请求转发给真正的模型提供商。路由的前提是知道“有哪些模型可用、各自走哪个 provider”，所以启动时它会读取配置构建一张“模型 → provider”的路由表；表为空时它不能猜，只能如实拒绝（No available models）。",
          "",
          "这其实是网关的**fail-fast 设计**——宁可拒绝服务也不静默转发到错误的 provider，避免“看起来在服务、实际全 502”。",
          "",
          "也因此修复路径固定：写配置（路由表有了内容）→ 重启网关（路由表重新加载）→ 再验证。改配置不重启不生效，因为路由表只在启动时构建一次，`stop && start` 才是完整的重载。",
          "",
          "> 实测环境：`ccr --version` 输出同样的提示（+ 闭环日志 step 2），符合预期；配置步骤见 站内文档。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：启动网关（然后浏览器打开管理界面添加 provider）",
          "ccr start",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 启动并打开管理界面 — **必做**",
          "",
          "```bash",
          "ccr start",
          "```",
          "",
          "按输出提示在浏览器打开管理界面（默认 localhost 端口）。",
          "",
          "### 2. 添加 provider + 模型 — **必做**",
          "",
          "在管理界面添加你的 provider（OpenAI/Anthropic/国产模型等）和模型。",
          "",
          "### 3. 或直接编辑配置 — **必做**",
          "",
          "`~/.claude-code-router/`（本版为 config.sqlite；见 guide.md 配置节），按格式写入 provider。",
          "",
          "### 4. 重启网关 — **必做**",
          "",
          "```bash",
          "ccr stop && ccr start",
          "```",
          "",
          "配置生效后再启动，不再报 No available models。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ccr start",
          "```",
          "",
          "不再出现 `No available models`、管理界面能列出模型即成功（实测：未配置时如实提示，配置后正常启动）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 首次运行 | 未配置 provider 必报，属正常 |",
          "| 管理界面 | 图形化添加 provider 最省事 |",
          "| 配置文件 | 本版 config.sqlite，按 guide.md 格式写 |",
          "| 改完不生效 | 必须 stop → start 重启网关 |",
          ""
        ],
        "症状": [
          "",
          "`ccr start` / `ccr serve` 提示 `No available models. Configure at least one provider...`；管理界面没有可用模型。",
          ""
        ],
        "原因": [
          "",
          "尚未配置任何 provider（`~/.claude-code-router/` 下 provider 配置为空）——首次运行正常现象，不是故障。",
          ""
        ],
        "解法": [
          "",
          "1. `ccr start` 后浏览器打开管理界面，添加 provider + 模型。",
          "2. 或直接编辑配置文件（`~/.claude-code-router/`，本版为 config.sqlite；见 guide.md 配置节）。",
          "3. 添加后重启网关（`ccr stop` → `ccr start`）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本市场实测：`ccr --version` 输出 `No available models. Configure at least one provider with a model before starting CCR Gateway`（+ 闭环日志 step 2），符合预期；配置步骤见 站内文档。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-3",
        "agent": "https://trove.xin/agent/problems/claude-code-router-3"
      }
    },
    {
      "id": "claude-code-router-4",
      "title": "本地模型 api_base_url 配置错误（Ollama 等）",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你按教程把网关切到本地模型（比如 Ollama），配置里填了 `api_base_url`，结果一发请求就报错：找不到模型（model not found）、连接被拒（connection refused），或者网关直接弹路由错误。教程里\"把地址填进去\"看着很简单，但地址填法差一点就完全不通。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "网关（claude-code-router）请求模型时不是直接请求 base 地址，而是请求一个**完整 URL：base + /v1/chat/completions**。只填 `http://localhost:11434` 这种 base 地址，网关拼不出完整路径，服务端要么 404、要么报连接拒绝；漏掉 `/v1` 段也一样，OpenAI 兼容端点通常挂在 `/v1` 下（上游 Issue #239 场景）。",
          "",
          "| 填法 | 结果 |",
          "|---|---|",
          "| `http://localhost:11434/v1/chat/completions`（完整） | ✅ 正常（Ollama 例） |",
          "| `http://localhost:11434`（只写 base） | ❌ 网关拼不出完整端点 |",
          "| `http://localhost:11434/chat/completions`（漏 /v1） | ❌ 路径不对，404/报错 |",
          "",
          "- 模型名必须和 `ollama list` 输出**逐字一致**（含 `:8b` 之类 tag，大小写也算）。",
          "- 改完配置必须**重启网关**才生效，改完不重启 = 看起来没改。",
          "",
          "网关调用模型走的是**标准 OpenAI 兼容协议**：客户端 → 网关 → 模型服务，最后一段请求是“POST 到模型服务的某个 URL”。这个 URL 不是随便填的，它由协议规定：OpenAI 兼容端点固定挂在 `{base}/v1/chat/completions`，base 是服务地址（含端口），`/v1/chat/completions` 是协议路径。",
          "",
          "网关配置里的 `api_base_url` 是“base + 路径”的合体——只填 base 等于告诉网关“端点不存在”，它拼不出协议要求的完整路径；漏 `/v1` 等于告诉它“端点在一个不存在的位置”。这就是“填法差一点，结果完全不通”的机制原因。模型名是第二道精确匹配：服务端按名字索引模型，名字对不上就是 model not found（含 tag、大小写）。",
          "",
          "还有一层容易被忽略：配置是启动时加载的，改完不重启，网关内存里还是旧配置——“看起来没改”其实是“没重载”。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认模型名，然后（以 Ollama 为例）把 api_base_url 改成完整地址并重启网关",
          "ollama list && ccr config set providers.ollama.api_base_url http://localhost:11434/v1/chat/completions && ccr restart",
          "```",
          "",
          "复制执行，注意把模型名与 `ollama list` 输出对齐（示例为 Ollama 默认 11434 端口）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认模型真在 — **必做**",
          "",
          "```bash",
          "ollama list",
          "```",
          "",
          "输出里要有你要用的模型名，比如 `qwen3:8b`。没有就先 `ollama pull <模型名>` 拉下来。",
          "",
          "### 2. 填完整 URL — **必做**",
          "",
          "把配置里的 `api_base_url` 改成（Ollama 例）：",
          "",
          "```bash",
          "http://localhost:11434/v1/chat/completions",
          "```",
          "",
          "关键点：**含端口、含 `/v1/chat/completions` 完整路径**，不要只填 base。",
          "",
          "### 3. 模型名逐字对齐 — **必做**",
          "",
          "配置里的模型名必须和 `ollama list` 输出逐字一致（含 tag）。拿不准就把 `ollama list` 第一列复制过去。",
          "",
          "### 4. 重启网关再测 — **必做**",
          "",
          "```bash",
          "ccr restart",
          "```",
          "",
          "重启后发一条测试请求，确认不再报错。"
        ],
        "怎么确认修好了": [
          "",
          "用 curl 直接打完整地址能拿到正常响应（实测可用形态）：",
          "",
          "```text",
          "curl http://localhost:11434/v1/chat/completions -d '{\"model\":\"<你的模型名>\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}' -H 'Content-Type: application/json'",
          "→ HTTP 200 + JSON 响应（含 choices）",
          "```",
          "",
          "网关侧再发同样请求，不再出现 model not found / connection refused。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Ollama 默认端口 | 11434，本地回环直接通，无需额外网络配置 |",
          "| 远程 / 容器内模型服务 | base 里 IP/端口按实际填，容器内注意 127.0.0.1 往往指容器自己 |",
          "| LM Studio / llama.cpp 等 | 同样要完整 `/v1/chat/completions` 路径，端口各不同，以服务启动日志为准 |",
          "| Windows / macOS | 配置与重启命令相同，路径写法一致 |",
          "| 未实测项 | Docker 形态与远程网关本市场未实测，如实标注 |",
          ""
        ],
        "症状": [
          "",
          "",
          "配置本地模型（Ollama 等）后请求失败：找不到模型 / 连接拒绝 / 路由报错。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "`api_base_url` 必须写完整 URL（含 `/v1/chat/completions` 路径）；只写 base（如 `http://localhost:11434`）或漏 `/v1` 都会失败（上游 Issue #239 场景）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 写完整 URL：`http://localhost:11434/v1/chat/completions`（Ollama 例）。",
          "2. 模型名与 `ollama list` 输出一致。",
          "3. 保存后重启网关再测。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 上游 Issue #239 + 项目本地模型配置文档。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-4",
        "agent": "https://trove.xin/agent/problems/claude-code-router-4"
      }
    },
    {
      "id": "claude-code-router-5",
      "title": "Docker 部署：端口 / volume / 权限",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你用 Docker 把网关跑起来了，容器显示在运行，但浏览器就是打不开；或者容器重建一次，配置全部消失；再或者启动时报 volume 权限错误。Docker 部署的三个常见坑一次踩全：端口映射不对、数据没持久化、容器用户和目录属主不匹配。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- 网关镜像里 Nginx 监听 8080，而 3456/3457/3459 是**容器内部端口**，只在容器内网互相访问，不需要映射到宿主机。只映射内部端口，宿主机上自然没有入口 → 浏览器打不开。",
          "- 不挂 named volume 时，配置写在容器可写层，容器一删就丢 → \"重启后配置丢失\"。",
          "- 镜像建议以 `-u 1000:1000` 运行；容器用户和挂载目录属主不一致时，容器内没权限读写 → 权限报错。",
          "",
          "| 现象 | 根因 |",
          "|---|---|",
          "| 浏览器打不开 | 只映射了内部端口，或没映射 8080 |",
          "| 重启后配置丢失 | 没挂 named volume |",
          "| volume 权限报错 | 容器用户与宿主机目录属主不匹配 |",
          "",
          "Docker 是**隔离运行时**，三个坑对应三层隔离：**网络隔离**——容器有自己的网络命名空间，容器内监听的端口默认只在容器内可见；`-p 宿主端口:容器端口` 是“打洞”动作，只映射了内部端口（3456/3457/3459）就相当于洞打在没人走的墙上，宿主机自然没有入口。",
          "",
          "**存储隔离**——容器可写层随容器生灭，数据放里面 = 一次性；named volume 是“把宿主机的一块存储挂进容器”，容器重建后重新挂上，数据还在。",
          "",
          "**用户隔离**——容器内进程以镜像指定的用户运行（如 1000:1000），挂载目录的属主是宿主机的；两边 ID 对不上，容器内就没权限读写。三个坑共享同一个方法论：**理解“容器内 ≠ 宿主机”，一切以“穿透边界”的方式显式打通**——端口要显式映射、数据要显式挂载、权限要显式对齐。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：按项目 docker/README.md 参数启动（8080 映射 + named volume + 指定用户）",
          "docker run -d --name ccr -p 8080:8080 -v ccr-data:/data -u 1000:1000 <镜像名>",
          "```",
          "",
          "以项目 docker/README.md 的实际镜像名/数据目录为准；这行解决\"打不开 + 丢配置 + 权限\"三个问题。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 查端口占用 — **必做**",
          "",
          "```bash",
          "docker ps",
          "```",
          "",
          "看 8080 是否已被占用；占用就换宿主端口 `-p 8081:8080`（容器内仍是 8080）。",
          "",
          "### 2. 用正确参数启动 — **必做**",
          "",
          "按项目 docker/README.md：只映射宿主需要的端口（通常 `-p 8080:8080`），用 named volume 保存数据，并加 `-u 1000:1000`：",
          "",
          "```bash",
          "docker run -d --name ccr -p 8080:8080 -v ccr-data:/data -u 1000:1000 <镜像名>",
          "```",
          "",
          "### 3. 浏览器验证 — **必做**",
          "",
          "```bash",
          "open http://localhost:8080",
          "```",
          "",
          "能打开管理界面即端口 OK（Windows 用手动打开浏览器）。",
          "",
          "### 4. 数据恢复 — **必做**",
          "",
          "如果之前没挂 volume 导致配置丢失：把旧数据目录挂到对应数据目录后重启：",
          "",
          "```bash",
          "docker run -d --name ccr -p 8080:8080 -v /path/to/old-data:/data -u 1000:1000 <镜像名>",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "容器启动后一条命令看状态与映射（预期输出形态）：",
          "",
          "```bash",
          "docker ps --filter name=ccr",
          "→ STATUS Up / PORTS 0.0.0.0:8080->8080/tcp",
          "```",
          "",
          "浏览器打开 `http://localhost:8080` 能出管理界面；重启容器后配置仍在（不丢）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 端口冲突 | `docker ps` 查占用，宿主侧换端口即可，容器内端口不用动 |",
          "| 数据持久化 | 必须 named volume；裸目录挂载注意属主 |",
          "| 权限报错 | 镜像文档建议 `-u 1000:1000`；也可换属主或改挂载目录 |",
          "| Windows Docker Desktop | 命令相同，浏览器手动访问 |",
          "| 未实测项 | 本市场未实测 Docker 形态（本地容器环境限制），以上按项目文档与网页案例整理 |",
          ""
        ],
        "症状": [
          "",
          "",
          "容器能起但浏览器打不开；重启后配置丢失；或 volume 权限报错。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "1. 只暴露 Nginx 8080：3456/3457/3459 是容器内部端口，不需要映射到宿主机。",
          "2. 未用 named volume：数据随容器重建丢失。",
          "3. 容器用户与 volume 属主不匹配（镜像文档建议 `-u 1000:1000`）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 按项目 docker/README.md 的参数启动：`-p 8080:8080` + named volume + `-u 1000:1000`。",
          "2. 端口冲突先 `docker ps` 查占用，换宿主端口（`-p 8081:8080`）即可。",
          "3. 数据恢复：volume 挂载到对应数据目录后重启。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 项目 docker/README.md；网页案例：CSDN（Docker 端口冲突/权限场景）。",
          "- 本市场未实测 Docker 形态（本地容器环境限制），如实标注。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-5",
        "agent": "https://trove.xin/agent/problems/claude-code-router-5"
      }
    },
    {
      "id": "claude-code-router-6",
      "title": "JSON / API 配置错误导致服务启动失败",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-code-router"
      ],
      "evidence": "site docs#6",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你改了 provider 或 profile 配置，然后 `ccr start` 直接报错、甚至崩溃；或者服务起来了，但管理界面 API 一路 500。改配置改出问题，服务起不来，又不知道错在哪一行。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "配置文件对语法和字段名都极其敏感：",
          "",
          "- **JSON 语法错误**：多一个逗号、少一个引号 → 解析直接失败 → 服务起不来。",
          "- **字段名/结构错误**：`api_base_url`、`type` 这类字段拼写错或层级不对，解析能过但运行时报错 → 管理界面 API 500。",
          "",
          "| 错误类型 | 表现 | 排查入口 |",
          "|---|---|---|",
          "| JSON 语法错误 | 启动即失败/崩溃 | JSON 校验工具 |",
          "| 字段名/结构错误 | 能启动但 API 500 | 对照项目配置文档 |",
          "",
          "配置真实写入面是 `~/.claude-code-router/config.sqlite`（本市场实测），改错后恢复备份即可回滚。",
          "",
          "配置驱动型服务启动时有两道独立的“闸门”：**语法解析**和**语义校验**。语法闸门在启动最前面——JSON 多一个逗号/少一个引号，解析器直接抛错，服务根本没机会起来，表现是“启动即失败/崩溃”；语义闸门在解析之后——字段拼写错或层级不对，JSON 解析能过，但服务按错误字段取值，运行到路由/API 层才炸，表现是“能启动但 500”。这就是“同样改错，表现完全不同”的原因。",
          "",
          "两个闸门对应两种定位工具：语法闸门用 `jq empty`/`python3 -m json.tool` 一次定位到行；语义闸门只能对照项目配置文档逐个核对字段。还有个确定性保障：**改配置前先备份**——配置文件是状态，备份让“改错”从不可逆变成可逆，恢复 = 删除 + 移回 + 重启三步固定。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先备份，再校验 JSON 语法，语法 OK 再重启（备份是唯一后悔药）",
          "cp -r ~/.claude-code-router ~/.claude-code-router.bak && jq empty <你的配置文件> && ccr restart",
          "```",
          "",
          "配置文件路径按你的实际配置（如 `~/.claude-code-router/config.json`）；`jq` 只校验 JSON。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先备份 — **必做**",
          "",
          "```bash",
          "cp -r ~/.claude-code-router ~/.claude-code-router.bak",
          "```",
          "",
          "改配置前永远先备份，改坏了有后悔药。",
          "",
          "### 2. 校验 JSON 语法 — **必做**",
          "",
          "```bash",
          "jq empty <你的配置文件>",
          "```",
          "",
          "没输出 = 语法 OK；有报错会指出第几行。没有 jq 可用 `python3 -m json.tool 文件`。",
          "",
          "### 3. 对照文档核对字段 — **必做**",
          "",
          "打开项目配置文档，逐个核对 `api_base_url`、`type` 等字段名与层级，别只凭记忆写。",
          "",
          "### 4. 改错就恢复备份并重启 — **必做**",
          "",
          "```bash",
          "rm -rf ~/.claude-code-router && mv ~/.claude-code-router.bak ~/.claude-code-router && ccr restart",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "配置修复后，服务能正常启动（预期输出形态）：",
          "",
          "```text",
          "ccr start",
          "→ 服务正常启动日志，无 JSON 解析错误 / 崩溃",
          "```",
          "",
          "管理界面 API 不再返回 500；改一条测试配置能正常保存并生效。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 配置文件位置 | 不同版本可能是 json/yaml/sqlite；本市场实测写入面为 `~/.claude-code-router/config.sqlite` |",
          "| 没有 jq | `python3 -m json.tool 文件` 同样校验 JSON；YAML 类配置用对应解析器 |",
          "| Windows / macOS | 备份与恢复命令相同，路径按用户目录 |",
          "| 改字段后 500 | 大概率字段名/层级不符，先对照文档再重启 |",
          ""
        ],
        "症状": [
          "",
          "",
          "修改配置后 `ccr start` 报错或崩溃；管理界面 API 返回 500。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "provider/profile 配置 JSON 语法错误或字段名不符（如 api_base_url、type 拼写/结构错误）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 改前备份配置文件（`~/.claude-code-router/`）。",
          "2. 改后用 JSON 校验工具检查语法。",
          "3. 对照项目配置文档核对字段；改错恢复备份后重启。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN 汇总篇（服务启动失败 / API / JSON 配置场景）。",
          "- 本市场实测：写面 = `~/.claude-code-router/config.sqlite`，配置修改前先备份该目录。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-code-router-6",
        "agent": "https://trove.xin/agent/problems/claude-code-router-6"
      }
    },
    {
      "id": "claude-json-multi-tool",
      "title": "多个工具都写 ~/.claude.json，一起装会冲突吗？（实测兼容）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "coexist-compat",
      "category_label": "组合与兼容",
      "applies_to": [
        "agent-governance-toolkit",
        "deepcode",
        "code-review-graph"
      ],
      "evidence": "site docs",
      "source": "本站实测沉淀（2026-08-27）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "想同时装 agt、deepcode、code-review-graph，它们安装时都会写 `~/.claude.json`（MCP/插件注册段），你担心互相覆盖导致某个工具失效或配置丢失。**实测结论：可以一起装，不冲突。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "三者都写 `~/.claude.json`，但写的是**不同 key/段**：",
          "",
          "| 工具 | 写入段 |",
          "|---|---|",
          "| agt | 策略/治理段 |",
          "| deepcode | 各自 MCP/工具注册段 |",
          "| code-review-graph | 各自 MCP/工具注册段 |",
          "",
          "JSON 按 key 合并，实测共存时结构完整、无互相覆盖。claude-mem 走独立写面 `~/.claude-mem/`，与三者实测兼容。",
          "",
          "`~/.claude.json` 是多工具共享的写面，但它的合并语义是「按 key 合并」：每个工具往自己的段写（agt 写策略/治理段，deepcode/code-review-graph 写各自的 MCP/工具注册段），读的时候各读各的 key——JSON 对象级合并不会互相覆盖，除非两个工具写同一个 key。",
          "",
          "确定性推论：共享写面安全与否取决于「key 空间是否隔离」，不是「是不是同一个文件」；实测三工具共存结构完整 = 它们的 key 空间互不重叠；判断新工具能否加入：查它写 `~/.claude.json` 的哪些 key，与现有工具比对；claude-mem 走独立目录 `~/.claude-mem/`（不同写面），与共享写面者天然隔离——写面分析比「同文件=冲突」的直觉准确得多。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：按各自官方命令安装，装完校验 JSON 可解析",
          "python3 -m json.tool ~/.claude.json >/dev/null && echo \"JSON OK\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 直接按各自官方命令安装 — **必做**",
          "",
          "agt、deepcode、code-review-graph、claude-mem 可同环境共存，无强制顺序。",
          "",
          "### 2. 装完校验 — **必做**",
          "",
          "```bash",
          "python3 -m json.tool ~/.claude.json",
          "```",
          "",
          "必须解析通过；再逐个跑工具冒烟（`agt lint-policy` / `crg build` / deepcode 诊断命令），均 exit 0 即正常。",
          "",
          "### 3. 升级后重跑冒烟 — **必做**",
          "",
          "升级任一工具后重跑冒烟；配置异常时先备份 `~/.claude.json` 再排查（`站内冲突预检说明` 可先看写面冲突）。"
        ],
        "怎么确认修好了": [
          "",
          "冒烟全绿（实测形态）：",
          "",
          "```text",
          "agt lint-policy → exit 0",
          "crg build → exit 0",
          "deepcode 诊断命令 → exit 0",
          "python3 -m json.tool ~/.claude.json → 解析通过",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| agt × deepcode | 共装互不干扰，deepcode 配置隔离 `~/.deepcode`（实测） |",
          "| agt × crg | 同工作区混用通过（agt lint-policy + crg build，实测） |",
          "| crg × deepcode | 同 venv 共装、双冒烟 exit 0、写面互不重叠（实测） |",
          "| claude-mem 组合 | 与三者分别实测兼容（combo 002/003） |",
          "| 升级后 | 重跑冒烟；异常先备份再排查 |",
          ""
        ],
        "症状": [
          "",
          "想同时装 agt、deepcode、code-review-graph，它们安装时都会写 `~/.claude.json`（MCP/插件注册段），担心互相覆盖导致某个工具失效或配置丢失。",
          ""
        ],
        "原因": [
          "",
          "三者都写 `~/.claude.json`，但写的是不同 key/段：agt 写策略/治理段、deepcode 与 code-review-graph 写各自 MCP/工具注册段；JSON 按 key 合并，实测共存时结构完整、无互相覆盖。claude-mem 走独立写面 `~/.claude-mem/`，与三者实测兼容。",
          ""
        ],
        "解法": [
          "",
          "1. 直接按各自官方命令安装（agt、deepcode、code-review-graph、claude-mem 可同环境共存）。",
          "2. 装完校验：`python3 -m json.tool ~/.claude.json` 必须解析通过；再逐个跑工具冒烟（`agt lint-policy` / `crg build` / deepcode 诊断命令），均 exit 0 即正常。",
          "3. 升级任一工具后重跑冒烟；配置出现异常时先备份 `~/.claude.json` 再排查（市场预检 `站内冲突预检说明` 可先看写面冲突）。",
          ""
        ],
        "验证证据": [
          "",
          "本站实测记录（2026-08-27）",
          "- claude-mem × deepcode、claude-mem × crg、agt × claude-mem 三对同批实测兼容（combo 002/003）。",
          "- 证据：站内文档（6 对 faces=~/.claude.json，tested_at 2026-08-28）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-json-multi-tool",
        "agent": "https://trove.xin/agent/problems/claude-json-multi-tool"
      }
    },
    {
      "id": "claude-mem-1",
      "title": "marketplace 克隆失败：SSH 认证失败 / 克隆卡死",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#1;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你在终端里执行 `claude-mem install --ide claude-code`，装到一半卡在 \"Adding marketplace…\"，等半天要么报 `SSH authentication failed`，要么直接没反应、克隆被重置。",
          "",
          "你以为是网络不行，换网络、关代理重试几次还是老样子——其实问题多半不在网络，而在**克隆走的是 SSH 通道，而你根本没有可用的 SSH key**（或 key 对应的账号状态异常）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Claude Code 装插件，本质上是在做一次 `git clone`：把插件仓库（claude-mem 上游约 271MB，含子模块）拉到本机。安装器默认用 SSH 地址（`git@github.com:...`）克隆：",
          "",
          "| 克隆通道 | 需要什么 | 国内直连表现 |",
          "|---|---|---|",
          "| SSH（默认） | 可用的 SSH key | 无 key 直接认证失败 |",
          "| HTTPS | 公开仓库无需凭据 | 大仓库慢/易断，建议走镜像 |",
          "",
          "- 走 SSH 就必须有能用的 key；没配 key、或 key 对应的 GitHub 账号异常，GitHub 直接拒绝认证 → 报 `SSH authentication failed`。",
          "- 国内直连 GitHub 时，这种大仓库的克隆经常中途断掉 → 表现为\"卡死\"或 `Recv failure: Connection reset by peer`。",
          "",
          "解法核心是 `git config --global url.<新地址>.insteadOf <旧地址>`：这是 git 的 **URL 改写**机制——凡是\"要访问旧地址\"的操作都改走新地址。公开仓库匿名就能克隆，根本不需要 key；改的只是克隆通道，插件本身的行为一行都不动，安全无副作用。",
          "",
          "安装器默认用 SSH 通道 clone，SSH 通道的认证机制是 **key 对**：git 把请求发给 GitHub 的 SSH 服务，GitHub 用你的公钥验证身份——没有 key 或 key 不被 GitHub 接受，认证直接失败（SSH authentication failed），这是“通道要求凭据”的问题，不是网络问题。",
          "",
          "而 HTTPS 通道对公开仓库**匿名即可**：不需要任何凭据，只要网络能到。",
          "",
          "git 的 `url.<新>.insteadOf <旧>` 是 **URL 改写机制**：git 在发起任何访问前，先查配置里的改写规则，命中的地址一律替换——它工作在“寻址层”，不碰插件的代码逻辑，所以“只换通道、不动行为”。这条机制还解释了为什么“安全无副作用”：改写的是 git 自己的地址解析，插件所有行为（clone 之后的内容处理）完全不变。",
          "",
          "判断路径固定：报 `SSH authentication failed` = 通道/凭据问题（改通道或配 key）→ 报 `Connection reset`/卡死 = 网络问题（换镜像/加速）→ 改完验证改写生效（`git config --get url.<新>.insteadOf`）。",
          "",
          "> 实测环境：claude-mem 插件本体 13.16.1（CLI 引导器版本号为 12.4.7，官方滞后现象），2026-08-28 本市场实测。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：强制 HTTPS + 国内加速（公开仓库匿名克隆，不需要 key；镜像不可用就删掉第一段）",
          "git config --global url.\"https://gh-proxy.com/https://github.com/\".insteadOf \"https://github.com/\" && git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\"",
          "```",
          "",
          "复制这一行到终端执行，然后重新跑安装命令即可。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认是不是这个问题 — **必做**",
          "",
          "看报错里有没有 `SSH authentication failed`，或安装长期卡在 \"Adding marketplace…\"。拿不准就手动测一下克隆通道：",
          "",
          "```bash",
          "git ls-remote git@github.com:thedotmack/claude-mem",
          "```",
          "",
          "如果提示 `Permission denied (publickey)` 或 `authentication failed`，就是这个问题。",
          "",
          "### 2. 强制走 HTTPS — **必做**",
          "",
          "```bash",
          "git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\"",
          "```",
          "",
          "这一行让 git 把所有 `git@github.com:...` 地址改写成 `https://github.com/...`。公开仓库匿名即可克隆，不需要 SSH key，认证失败直接消失。全局配置对本机所有 git 操作生效，无害。",
          "",
          "### 3. 国内加速（可选，推荐） — **进阶**",
          "",
          "```bash",
          "git config --global url.\"https://gh-proxy.com/https://github.com/\".insteadOf \"https://github.com/\"",
          "```",
          "",
          "国内直连 GitHub 的克隆经常中断。这行把 HTTPS 地址再改写成 gh-proxy 镜像。**镜像偶尔会挂或变慢：如果加速后反而失败/更慢，跳过这一步，只保留第 2 步的强制 HTTPS 即可**，也可以换成你所在网络可达的其它镜像。这条 insteadOf 会自动应用到以后所有 GitHub 克隆（包括你其它项目），不影响任何其它行为。",
          "",
          "### 4. 重新安装 — **必做**",
          "",
          "```bash",
          "claude-mem install --ide claude-code",
          "```",
          "",
          "这次应该很快看到 `SSH not configured, cloning via HTTPS`，然后 `✔ Successfully added marketplace: thedotmack`。"
        ],
        "怎么确认修好了": [
          "",
          "安装命令出现下面两行就算成功（实测输出）：",
          "",
          "```text",
          "SSH not configured, cloning via HTTPS",
          "✔ Successfully added marketplace: thedotmack",
          "✔ Successfully installed plugin: claude-mem@thedotmack (scope: user)",
          "```",
          "",
          "再跑一次 `claude-mem --version` 能正常输出版本号（实测环境输出 CLI 12.4.7，插件本体 13.16.1，属官方现象）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows / PowerShell | git 配置命令完全相同，直接执行，无需管理员权限 |",
          "| macOS | 同上，macOS 自带 git，终端直接执行 |",
          "| 公司代理 / 校园网 | 镜像慢/不可用就只留\"强制 HTTPS\"，或换可达镜像 |",
          "| 私有仓库 | insteadOf 只改克隆通道不改鉴权；私有仓库仍需要 HTTPS 凭据或 SSH key |",
          "| 只想这次生效 | `-c` 只对单条命令临时注入：`git -c url.\"https://github.com/\".insteadOf=git@github.com: ls-remote git@github.com:thedotmack/claude-mem`；注意它管不到安装器内部的克隆 |",
          "| 想撤销 | `git config --global --unset-all url.https://gh-proxy.com/https://github.com/.insteadOf` 和 `git config --global --unset-all url.https://github.com/.insteadOf` 分别执行 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`claude-mem install --ide claude-code` 在 \"Adding marketplace…\" 处报错或长期卡住：",
          "",
          "```",
          "✘ Failed to add marketplace: Failed to clone marketplace repository: SSH authentication failed.",
          "Please ensure your SSH keys are configured for GitHub, or use an HTTPS URL instead.",
          "```",
          "",
          "或直连时克隆无进展后报 `Recv failure: Connection reset by peer`。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "Claude Code 插件安装 = git clone 上游仓库 thedotmack/claude-mem（浅克隆 + 递归子模块；实测仓库约 271MB），安装器默认走 SSH URL（`git@github.com:...`）。无可用 SSH key（或 key 对应账号状态异常）时认证失败；国内直连 GitHub 时大仓库克隆容易中断。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 强制 HTTPS（公开仓库匿名即可克隆，不需要 key）：",
          "   `git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\"`",
          "2. 国内加速（2026-08-28 实测可用）：",
          "   `git config --global url.\"https://gh-proxy.com/https://github.com/\".insteadOf \"https://github.com/\"`",
          "3. CLI 本身走镜像源：`npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem`",
          "4. 以上只改 git clone 的 URL 通道，不改插件任何行为；不用给任何账号授权。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测 2026-08-28（实测记录（本地留存））：",
          "  - 无改写时：SSH 认证失败（key 对应账号状态异常）→ install FAIL；",
          "  - 加改写后：`SSH not configured, cloning via HTTPS` → `✔ Successfully added marketplace: thedotmack` → `✔ Successfully installed plugin: claude-mem@thedotmack (scope: user)` → `claude-mem installed successfully!` → install exit=0；",
          "  - 直连 90s 未完成 vs gh-proxy 加速后 60s 内克隆完成（单次实测；仓库实测 271MB）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-1",
        "agent": "https://trove.xin/agent/problems/claude-mem-1"
      }
    },
    {
      "id": "claude-mem-2",
      "title": "worker/UI 端口占用：\"Is port 37777 in use?\"",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Windows 重启之后（或装了多个 Agent 工具后），`claude-mem` 的 worker 启动失败，报 `Is port 37777 in use?` 或 `Address already in use`，记忆 UI 打不开。你以为是软件坏了，重装一遍还是老样子——其实是**端口被残留进程占了**，跟软件本身无关。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "claude-mem 的 worker 是一个本地常驻进程，它要监听一个固定端口供 UI/插件通信。端口被占的原因主要有三类：",
          "",
          "| 原因 | 说明 |",
          "|---|---|",
          "| 残留进程 | Windows 重启后旧 worker 没退干净，还占着端口 |",
          "| 其它程序 | 别的工具恰好用了同一端口 |",
          "| TIME_WAIT | 端口刚释放，系统还在等 socket 超时，立即重启会被拒 |",
          "",
          "- 注意端口号**随版本变化**：老资料写的是 37777，本市场实测版本（插件 13.16.1）安装输出是 `Worker port: 37700`——排查前先看实际输出，别按旧资料找错端口。",
          "- 解决思路不是\"重装\"，而是：**先看端口 → 杀掉占用者 → 必要时换端口**。",
          "",
          "常驻 worker 与 UI/插件通信靠的是**固定端口契约**：worker 启动时绑定一个端口，UI/插件按约定连这个端口——端口就是它们之间的“通信地址”。端口是排他资源：同一时刻同一地址只能被一个 socket 绑定，第二个绑定者直接收到 `Address already in use`。",
          "",
          "三个占用来源对应三种生命周期问题：**残留进程**（Windows 重启后旧 worker 没退干净，进程还在、端口还握着）、**别家程序**（巧合撞端口）、**TIME_WAIT**（端口刚释放，内核按 TCP 规范要等一段时间才能重新绑定——这是协议要求，不是故障）。",
          "",
          "理解“端口即契约”后，两个推论自然成立：一是**端口号随版本变**（契约由版本决定，本版 37700、老资料 37777，以安装输出为准），二是**改端口是合法的**（端口是 claude-mem 自己的契约，换一个不影响别人，改完重启 worker 即可）。",
          "",
          "> 实测环境：claude-mem 插件本体 13.16.1，2026-08-28 本市场实测。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：按本版实际端口清理占用（默认 37700；旧版资料为 37777，先看安装输出确认）",
          "kill -9 $(lsof -t -i:37700) 2>/dev/null; pkill -f claude-mem 2>/dev/null; claude-mem status",
          "```",
          "",
          "复制执行后 worker 会重新拉起。Windows 没有 `lsof` 时用下面的分步法（PowerShell 版）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认实际端口 — **必做**",
          "",
          "看安装输出或 `claude-mem status` 里写的 `Worker port:` 是多少，别默认 37777：",
          "",
          "```bash",
          "lsof -i :37700   # 按实际端口替换；没有输出 = 端口没被占",
          "```",
          "",
          "Windows PowerShell 用：",
          "",
          "```powershell",
          "netstat -ano | findstr 37700",
          "```",
          "",
          "### 2. 杀掉占用进程 — **必做**",
          "",
          "macOS/Linux：",
          "",
          "```bash",
          "kill -9 $(lsof -t -i:37700)",
          "```",
          "",
          "Windows（用上一步 netstat 拿到的 PID）：",
          "",
          "```powershell",
          "taskkill /F /PID <PID>",
          "```",
          "",
          "### 3. 还不行就换端口 — **必做**",
          "",
          "设环境变量，或用配置改端口：",
          "",
          "```bash",
          "export CLAUDE_MEM_WORKER_PORT=37701   # 或写入 ~/.claude-mem/settings.json",
          "```",
          "",
          "> 端口是 claude-mem 自己用的，改端口不影响你的其它项目；改完重启 worker 即可。"
        ],
        "怎么确认修好了": [
          "",
          "`claude-mem status` 能正常输出版本和 worker 状态，且记忆 UI 能打开，就算成功（实测输出示意）：",
          "",
          "```text",
          "Worker port: 37700",
          "✔ worker running",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows / PowerShell | 没有 `lsof`，用 `netstat -ano \\| findstr 37700` 查 PID，`taskkill /F /PID` 杀 |",
          "| macOS / Linux | `lsof -i :37700` + `kill -9` 即可，与上面命令一致 |",
          "| 重启后反复出现 | 旧 worker 没退干净，先 `pkill -f claude-mem` 再启动 |",
          "| 端口号拿不准 | 以安装输出 `Worker port:` 为准；旧版资料 37777 别照抄 |",
          ""
        ],
        "症状": [
          "",
          "worker 启动失败，报端口被占用（Windows 重启后常见）；记忆 UI 打不开。",
          ""
        ],
        "原因": [
          "",
          "旧版/部分版本 worker 固定端口 37777，被残留进程或其它程序占用；僵尸 socket（TIME_WAIT）未释放。",
          ""
        ],
        "解法": [
          "",
          "1. 先确认实际端口（安装输出 / `claude-mem status` 为准）：`lsof -i :37700`（本版）或 `lsof -i :37777`（旧版资料）。",
          "2. 清理占用：`kill -9 $(lsof -t -i:37700)`（按实际端口替换）。",
          "3. 改端口：`CLAUDE_MEM_WORKER_PORT` 环境变量，或 `~/.claude-mem/settings.json` 中配置。",
          ""
        ],
        "验证证据": [
          "",
          "- 上游 Issue #415/#380（2025-12，Windows 重启后端口占用场景）。",
          "- 本市场实测：本版（插件 13.16.1）安装输出 `Worker port: 37700`——端口随版本变化，排查前先看实际输出，避免按旧资料找错端口。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-2",
        "agent": "https://trove.xin/agent/problems/claude-mem-2"
      }
    },
    {
      "id": "claude-mem-3",
      "title": "首会话重复 SessionStart hook 错误（worker 竞态）",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "刚装好 claude-mem，每次开新会话都看到 2 个 SessionStart hook 错误，记忆有时不注入。你以为是装坏了，重装一遍还是这样——其实是 **worker 启动竞态**：多个 hook 同时触发时 worker 还没就绪（上游 Issue #1447），属安装后的正常现象，不是故障。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "claude-mem 的 worker 是常驻进程，SessionStart hook 会找它要记忆。刚装完或刚开机时 worker 还在启动中，hook 触发得太快就报错。安装完成时的提示 `worker not yet ready -- still starting up` 就是这种情况（实测见过）——**看 `claude-mem status` 确认，而不是重装**。",
          "",
          "claude-mem 是“**主进程 + 常驻 worker**”架构：主进程（Claude Code 插件）通过 hook 向 worker 要记忆，worker 是独立进程、启动需要时间。会话开始的 SessionStart hook 触发时机在“客户端事件发生”时，不等待 worker 就绪——刚装完/刚开机时 worker 还在启动中，hook 先到、worker 未就绪，于是报错。",
          "",
          "这是典型的**竞态**（上游 Issue #1447）：两个动作（hook 触发、worker 就绪）之间没有同步，谁先到取决于时序而不是谁对谁错。",
          "",
          "修复思路因此不是修配置，而是**消除竞态窗口**：开会话前先 `claude-mem start` 预热 + `claude-mem status` 确认就绪，把“hook 触发”挪到“worker 就绪”之后。这也解释了为什么“重装没用”——重装重置的是安装状态，竞态窗口照样存在。",
          "",
          "> 实测环境：claude-mem 插件本体 13.16.1，2026-08-28 本市场实测。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：开会话前预热 worker",
          "claude-mem start && claude-mem status",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 预热 worker — **必做**",
          "",
          "```bash",
          "claude-mem start",
          "```",
          "",
          "（npm 全局装的老版本用 `npx claude-mem start`。）",
          "",
          "### 2. 确认就绪 — **必做**",
          "",
          "```bash",
          "claude-mem status",
          "```",
          "",
          "显示 worker 就绪再开会话，SessionStart hook 就不会抢跑。",
          "",
          "### 3. 启动失败时清理残留进程 — **必做**",
          "",
          "```bash",
          "lsof -ti :37700 | xargs -r kill -9; pkill -f claude-mem; claude-mem start",
          "```",
          "",
          "按实际端口替换（见端口占用条目：本版 37700，旧资料 37777）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "claude-mem status",
          "```",
          "",
          "worker 显示就绪、新会话不再报 SessionStart hook 错误即成功。实测：安装完成提示 `worker not yet ready -- still starting up` 属正常，`status` 确认即可。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 刚装完首次使用 | 预热 worker 再开会话 |",
          "| 重启后 | 同样先 `claude-mem start` + status |",
          "| 多个 hook 同时触发 | 上游 #1447 竞态，预热可避免 |",
          "| 老版本（npm/pm2） | `npx claude-mem start`；旧版用 pm2 管理 |",
          ""
        ],
        "症状": [
          "",
          "每次新会话开始出现 2 个 SessionStart hook 错误；记忆有时不注入。",
          ""
        ],
        "原因": [
          "",
          "worker 启动竞态（上游 Issue #1447）：多个 hook 同时触发时 worker 尚未就绪。",
          ""
        ],
        "解法": [
          "",
          "1. 预预热：开会话前 `claude-mem start`（或 `npx claude-mem start`），确认 `claude-mem status` 显示 worker 就绪。",
          "2. 启动失败时先清理残留 worker 进程再 start（见症状 2 的 lsof/kill 方法）。",
          ""
        ],
        "验证证据": [
          "",
          "- 上游 Issue #1447；上游项目方文档 Troubleshooting（lsof/kill 清理残留进程）。",
          "- 本市场实测：安装完成提示 `worker not yet ready -- still starting up; check claude-mem status later`——属正常提示，按 status 确认即可。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-3",
        "agent": "https://trove.xin/agent/problems/claude-mem-3"
      }
    },
    {
      "id": "claude-mem-4",
      "title": "npm 依赖安装失败",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`npm install -g claude-mem` 报错或后台静默失败，装完 `claude-mem` 命令不可用。你重试几次还是不行——多数是**网络/源的问题**，少数是**运行时缺失**：新版 claude-mem（v7.1.0 起）把 worker 从 pm2 换成了 Bun，机器上没有 Bun 就会在安装或首次启动时出问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- 安装失败主因：npm 默认官方源在国内不稳定，依赖树拉不全；",
          "- 静默失败：部分网络错误 npm 只给警告，装完才发现命令没有；",
          "- 运行时差异：旧资料写\"用 pm2 管理 worker\"（旧版），新版依赖 **Bun 运行时**——按旧教程查 pm2 会找错方向。",
          "",
          "`npm install -g` 的成功标准只是“包的二进制被链接进全局 bin 目录”，npm 不负责检查包在运行时还需要什么——它只管 JS 依赖树。",
          "",
          "claude-mem 的 worker 是独立进程，v7.1.0 起由 Bun 运行时承载，而 npm 不会替你验证 Bun 是否存在：装完 `claude-mem` 命令本身能出来（命令只是 JS 包的入口），真正跑 worker 时才在 Bun 上崩。",
          "",
          "这就是“装完命令不可用”的两条完整链路：网络问题 → 依赖树拉不全（npm 经常只给警告不报错）→ 命令缺失；运行时问题 → 命令在、worker 起不来。",
          "",
          "所以确认顺序固定为三步：`claude-mem --version` 看命令在不在（网络层）→ `bun --version` 看运行时在不在（运行时层）→ `claude-mem status` 看 worker 是否就绪（服务层），哪层断补哪层。",
          "",
          "> 实测环境：2026-08-28 本市场实测：npmmirror 全局安装成功（首次约 50s，缓存后约 5s）；Bun 已装。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：换国内源重装（装完确认版本）",
          "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem && claude-mem --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 换源重试 — **必做**",
          "",
          "```bash",
          "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem",
          "```",
          "",
          "实测：npmmirror 全局安装成功（首次约 50s，缓存后约 5s）。",
          "",
          "### 2. 依赖异常时强装 + 重启 worker — **必做**",
          "",
          "```bash",
          "npm ci && npm install --force",
          "claude-mem restart",
          "```",
          "",
          "### 3. 确认运行时（Bun） — **必做**",
          "",
          "```bash",
          "bun --version",
          "```",
          "",
          "缺 Bun 就先装 Bun（`curl -fsSL https://bun.sh/install | bash`）；旧资料里的 pm2 场景只对应旧版本，别照抄。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "claude-mem --version && claude-mem status",
          "```",
          "",
          "命令可用、worker 就绪即成功（实测：换源后安装成功，版本正常输出）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 国内网络 | 换 npmmirror 源重装 |",
          "| 新版（v7.1.0+） | 需要 Bun 运行时，`bun --version` 确认 |",
          "| 旧版（pm2 时代） | 按旧资料用 pm2 管理 |",
          "| 静默失败 | 装完先 `claude-mem --version` 确认命令存在 |",
          ""
        ],
        "症状": [
          "",
          "`npm install -g claude-mem` 报错或后台静默失败，claude-mem 命令不可用。",
          ""
        ],
        "原因": [
          "",
          "依赖树/网络问题；旧版使用 pm2 管理 worker（v7.1.0 起 PM2→Bun，需要 Bun 运行时）。",
          ""
        ],
        "解法": [
          "",
          "1. 换源重试：`npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem`。",
          "2. 依赖异常时 `npm ci` 或 `npm install --force` 后重启 worker。",
          "3. 确认运行时：`bun --version`（缺 Bun 时装 Bun；旧资料里的 pm2 场景对应旧版本）。",
          ""
        ],
        "验证证据": [
          "",
          "- 上游 Issue #63；CSDN 2026-03（旧版 pm2 重启场景）。",
          "- 本市场实测 2026-08-28：npmmirror 全局安装成功（首次约 50s，缓存后约 5s）；Bun 已装。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-4",
        "agent": "https://trove.xin/agent/problems/claude-mem-4"
      }
    },
    {
      "id": "claude-mem-5",
      "title": "Chroma 进程泄漏 / zombie 链",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "长期使用后内存/进程数逐渐膨胀，`ps` 里出现多个 chroma 残留进程，机器越来越卡。你以为是自己开太多会话——其实是**旧版的 process-tree 清理缺失**（上游 Issue #2959/#3137）：worker 退出时没有把子进程树清干净，越积越多。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "claude-mem 的 worker 会拉起 chroma（向量库）等子进程。旧版本在退出/重启时没有正确清理整棵进程树，导致子进程变成残留（zombie 链）。2026-08-25 发布的 v13.16.1 已修复 process-tree cleanup——**升级插件**是第一解法，而不是手动杀进程硬撑。",
          "",
          "> 实测环境：本市场收录版本 = 13.16.1（含该修复），2026-08-28。",
          "残留进程的本质是**进程树清理链断裂**：worker 直接 spawn chroma 等子进程，旧版退出时只回收自己，没有递归杀掉整棵子进程树。被遗留的 chroma 不会自己退出——它是长驻服务，父进程没了之后被 init 收养，继续占着内存和端口。所以\"越用越卡\"不是幻觉，是每次会话退出都漏掉几个子进程，线性累积。",
          "",
          "`pkill -f` 是按命令行特征匹配整条链清理的兜底手段，但根治必须在退出路径上补 tree-kill——这正是 v13.16.1 修的。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：清残留 + 重启 worker（再升级插件到 13.16.1 根治）",
          "pkill -f chroma; pkill -f claude-mem; claude-mem start",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 升级插件（根治） — **必做**",
          "",
          "```bash",
          "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem",
          "claude-mem update",
          "```",
          "",
          "v13.16.1（2026-08-25 release）修复 process-tree cleanup。",
          "",
          "### 2. 清理残留进程 — **必做**",
          "",
          "```bash",
          "pkill -f chroma; pkill -f claude-mem",
          "```",
          "",
          "### 3. 重启 worker — **必做**",
          "",
          "```bash",
          "claude-mem start && claude-mem status",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ps aux | grep -c chroma; claude-mem status",
          "```",
          "",
          "重启后 chroma 残留数归零、worker 就绪即成功（升级后不再累积）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 长期使用 | 升级 13.16.1+ 根治，勿只靠手动杀 |",
          "| 已膨胀 | 先清理残留再升级，顺序不反 |",
          "| Windows | `taskkill /F /IM chroma*` 等量替代 pkill |",
          ""
        ],
        "症状": [
          "",
          "长期使用后内存/进程膨胀，出现多个 chroma 残留进程。",
          ""
        ],
        "原因": [
          "",
          "旧版 process-tree 清理缺失（上游 Issue #2959/#3137）。",
          ""
        ],
        "解法": [
          "",
          "1. 升级插件到 v13.16.1（2026-08-25 release 修复 process-tree cleanup）。",
          "2. 手动清理残留进程后重启 worker。",
          ""
        ],
        "验证证据": [
          "",
          "- 上游 Issue #2959/#3137。",
          "- 本市场收录版本 = 13.16.1（含该修复）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-5",
        "agent": "https://trove.xin/agent/problems/claude-mem-5"
      }
    },
    {
      "id": "claude-mem-6",
      "title": "第三方模型鉴权切换后 \"No previous sessions found\"",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#6",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "用 DeepSeek 等第三方模型，从 OAuth 切到 API Key 之后，`/mem-search` 报 `No previous sessions found`——明明之前有记忆。",
          "",
          "你以为是记忆丢了，其实是**鉴权信息不一致**：worker 不读取 `ANTHROPIC_BASE_URL` / `AUTH_TOKEN` 环境变量，需要手动同步到 `~/.claude-mem/.env`（上游 Issue #2666）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "claude-mem 的 worker 自己维护一份环境配置（`~/.claude-mem/.env`），不跟随你 shell 里的环境变量。切换鉴权方式后，shell 里已经是新配置，但 worker 还拿着旧的——于是会话捕获失败，搜索自然没有结果。同步 .env + 重启 worker + 重新触发一次会话即可。",
          "",
          "> 来源：[CSDN zxqsoftware/161293220](https://blog.csdn.net/zxqsoftware/article/details/161293220)（2026-05）+ 上游 Issue #2666。",
          "关键认知是：**记忆捕获是旁路，不是主进程的一部分**。claude-mem 的 worker 是独立进程，它订阅会话事件、自己维护一份鉴权配置（`~/.claude-mem/.env`）——你 shell 里的 `ANTHROPIC_*` 环境变量只影响 Claude Code 主进程，worker 一概不读。",
          "",
          "切鉴权方式后，主进程换上新 key 正常聊天，worker 却还在用旧配置，于是\"捕获链\"是断的：会话根本没被记下来，搜索当然\"没有以前的会话\"。",
          "",
          "记忆没有丢，是链路没通。所以修复必须三步齐做：同步 `.env` → 重启 worker（重新加载配置）→ 重触发一次会话（让新配置真正跑一遍捕获）。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：把 BASE_URL / AUTH_TOKEN 写进 worker 配置并重启",
          "echo \"BASE_URL=$ANTHROPIC_BASE_URL\" > ~/.claude-mem/.env && echo \"AUTH_TOKEN=$ANTHROPIC_AUTH_TOKEN\" >> ~/.claude-mem/.env && claude-mem restart",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 同步鉴权到 worker 配置 — **必做**",
          "",
          "编辑 `~/.claude-mem/.env`，把当前的 `BASE_URL` / `AUTH_TOKEN`（或 `ANTHROPIC_*` 对应值）写进去，与 shell 环境保持一致。",
          "",
          "### 2. 重启 worker — **必做**",
          "",
          "```bash",
          "claude-mem restart",
          "```",
          "",
          "### 3. 重新触发一次会话 — **必做**",
          "",
          "开一个新会话让它捕获，再 `/mem-search` 验证。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "claude-mem status && claude-mem search \"上次的话题\"",
          "```",
          "",
          "worker 就绪、搜索能返回结果即成功（切换鉴权后同步 .env + 重启是核心）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| OAuth → API Key | 必须同步 ~/.claude-mem/.env 后重启 |",
          "| DeepSeek 等第三方 | 以实际 BASE_URL/AUTH_TOKEN 为准 |",
          "| 切换后无记忆 | 记忆没丢，捕获链没通，重触发会话 |",
          ""
        ],
        "症状": [
          "",
          "DeepSeek 等第三方模型从 OAuth 切到 API Key 后，/mem-search 报 No previous sessions found。",
          ""
        ],
        "原因": [
          "",
          "鉴权信息不一致；worker 不读取 `ANTHROPIC_BASE_URL`/`AUTH_TOKEN` 环境变量，需要手动同步到 `~/.claude-mem/.env`（上游 Issue #2666）。",
          ""
        ],
        "解法": [
          "",
          "1. 手动同步 `~/.claude-mem/.env` 的 BASE_URL / AUTH_TOKEN。",
          "2. 重启 worker（`claude-mem restart`）。",
          "3. 重新触发一次会话捕获后再搜索。",
          ""
        ],
        "验证证据": [
          "",
          "- [CSDN zxqsoftware/161293220](https://blog.csdn.net/zxqsoftware/article/details/161293220)（2026-05）。",
          "- 上游 Issue #2666。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-6",
        "agent": "https://trove.xin/agent/problems/claude-mem-6"
      }
    },
    {
      "id": "claude-mem-7",
      "title": "opencode 安装 API 不匹配",
      "domain": "session",
      "domain_label": "会话与记忆",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "claude-mem"
      ],
      "evidence": "site docs#7;local repro log;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "在 opencode 里安装 claude-mem 插件报 API 不匹配 / 加载失败。别急着怀疑自己操作错了——这是 **opencode 插件 API 与 claude-mem 插件实现版本不一致**（CSDN 2026-05 案例），插件的主形态是 Claude Code，opencode 形态兼容性由上游推进。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "opencode 与 Claude Code 的插件 API 不完全一致，claude-mem 的实现按 Claude Code 的 API 版本走；两边版本对不上时 opencode 侧就加载失败。上游没有承诺 opencode 首发兼容，所以这不是安装姿势问题。",
          "",
          "插件不是“一个文件拷过去就能跑”，它要对接客户端暴露的插件 API（生命周期钩子、工具注册、上下文接口等）。API 是版本化的：客户端升级会加新接口或改签名，插件按旧接口写的实现就会加载失败。",
          "",
          "claude-mem 的实现跟着 Claude Code 的 API 版本走，opencode 的 API 和 Claude Code 不完全一致——两边版本一对不上，opencode 侧在加载阶段就报 API 不匹配，这是“客户端-插件契约”层面的不兼容，不是安装姿势问题。",
          "",
          "由此得出两个确定性判断：一是判断插件版本别用 `claude-mem --version`（那是 npm CLI 的版本号），要看插件目录 `~/.claude/plugins/cache/thedotmack/claude-mem/<ver>`（双轨版本号）；二是 opencode 场景的修复路径只能是“等上游更新”或“换兼容实现”，因为契约在别人手里。",
          "",
          "> 本市场未实测 opencode 形态（无登录态），如实标注；Claude Code 形态已完整实测（安装/卸载全链路，2026-08-28）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认插件是否适用于你的客户端",
          "claude-mem doctor",
          "```",
          "",
          "Claude Code 场景可直接用；opencode 场景优先等插件更新或换兼容实现。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认客户端场景 — **必做**",
          "",
          "Claude Code（插件主形态）：正常安装使用，本市场实测安装/卸载全链路通过（install exit=0 → uninstall 零残留）。",
          "",
          "### 2. opencode 场景 — **必做**",
          "",
          "- 等待插件更新后再试 opencode 形态；",
          "- 或替换为兼容实现（如官方支持的 memory 方案）。",
          "",
          "### 3. 版本核对 — **必做**",
          "",
          "排查/升级判断以插件目录 `~/.claude/plugins/cache/thedotmack/claude-mem/<ver>` 为准，不要用 `claude-mem --version` 当插件版本（npm CLI 12.4.7 ≠ 插件 13.16.1，双轨版本号）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "claude-mem doctor && claude-mem status",
          "```",
          "",
          "Claude Code 场景 worker 就绪即成功（实测安装后 `Worker port: 37700`、Installation Complete、exit=0）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Claude Code（主形态） | 正常，本市场实测通过 |",
          "| opencode | API 不匹配，等上游更新或换兼容实现 |",
          "| 版本判断 | 以插件目录版本号为准，CLI 版本≠插件版本 |",
          ""
        ],
        "症状": [
          "",
          "opencode 下安装 claude-mem 插件报 API 不匹配/加载失败。",
          ""
        ],
        "原因": [
          "",
          "opencode 插件 API 与 claude-mem 插件实现版本不一致（CSDN 2026-05 案例）。",
          ""
        ],
        "解法": [
          "",
          "1. 替换兼容实现，或优先在 Claude Code 场景使用（插件主形态）。",
          "2. 等待插件更新后再试 opencode 形态。",
          ""
        ],
        "验证证据": [
          "",
          "- CSDN 2026-05 案例。",
          "- 本市场未实测 opencode 形态（无登录态），如实标注。",
          "本站实测记录（2026-08-28）",
          "- 完整日志：实测记录（本地留存）；CLI/写面证据：实测记录（本地留存）。",
          "- 未能实测：真实会话捕获/记忆写入（需登录会话）、adopt/cleanup 实跑、opencode 形态。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/claude-mem-7",
        "agent": "https://trove.xin/agent/problems/claude-mem-7"
      }
    },
    {
      "id": "code-review-consensus",
      "title": "Code Review 变成了形式主义，还拖慢迭代——怎么让它真正有用？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先说我们团队\"最黑\"的一个迭代：14 天一个迭代，3.5 天卡在评审等待上。",
          "",
          "不是评审严格——是评审流程本身变成了阻塞点。更讽刺的是，那 3.5 天里真正被认真看过的代码不足总量的 40%，剩下的全在\"LGTM\"里被快速略过。开发在等评审，评审人在等开发\"别改太频繁\"，两边互相等，一个迭代一半时间在空转。我一度觉得 Code Review 这个制度本身就该被干掉。",
          "",
          "后来我又见过另一款\"死法\"，比拖慢更阴：评审完全变成走流程。大家为了完成 KPI 随便打个 LGTM，有人甚至不看代码直接点确认。我们做过一次实验，随机抽了 50 个 PR，人工评估每条评论的质量——只有 35% 的评论包含实质性的逻辑或设计建议，其余全是\"格式问题\"\"补个注释\"或者直接 LGTM。",
          "",
          "还有第三种死法，我差点亲手把团队关系搞死：刚当上小组长那会儿，我满脑子\"指出问题\"就是负责。结果一个同事被我批了三次之后，开始绕着我走，PR 都不给我看了。Code Review 本来是让项目更稳的，结果成了团队关系的坟头。",
          "",
          "如果你也在经历这些——评审排队卡死迭代；或者评审已经变成\"点赞仪式\"，大家心照不宣；或者你总觉得\"提意见\"像在得罪人——这篇文章是写给同类人的。它不是评审规范模板，是那些真刀真枪改过评审流程的人，用数据和个人事故换来的共识：**评审既不是检查站，也不是仪式，它是一组放在关键节点上的\"减速带\"——让你慢一点点，但永远不会让整条路停下来等。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：评审为什么普遍翻车？我把见过的团队翻车方式归成四类，几乎每类都有机制原因，不是某个人不努力：",
          "",
          "**第一类：评审被当成\"检查站\"。** 代码写完了才评审，所有问题攒到最后一次性爆出来。这是最反直觉的一点——评审放在最后，等于把\"纠错成本\"抬到最高。改一行代码的成本，在写的时候是 1，提交后是 10，上线后是 100。检查站式的评审让所有人养成\"先写再说，反正有人看\"的心态，评审人则成了全团队最后的质检员，又累又招恨。",
          "",
          "**第二类：MR 太大，评审人根本看不动。** 一个 2000 行的 MR 和一个 200 行的 MR，评审难度不是 10 倍关系，是 30 倍关系。人脑的注意力和工作记忆是有上限的，超过几百行，评审就退化成\"看个大概\"。大 MR 不是\"工作量问题\"，是\"评审失效问题\"——它从物理上让认真评审变得不可能。",
          "",
          "**第三类：评审没有激励，只有义务。** 大家默认\"评审是额外工作\"，做完没奖励，漏了问题要背锅。于是最优策略就是\"快速 LGTM，别给自己惹麻烦\"。再加上人情因素——熟人互捧、不好意思挑刺——形式主义就长出来了。这不是道德问题，是激励结构问题：义务没有回报，质量必然让位于速度。",
          "",
          "**第四类（最新的）：AI 把\"批改作业\"这件事干掉了。** Linter、Formatter、AI 检查器现在能覆盖大部分表面正确性——命名、格式、early return、类型定义放哪，机器一眼扫完还不出错。以前评审人像老师改卷子，现在卷子机器都改完了，人类评审员忽然发现自己失业了。很多团队的评审随之彻底躺平：\"反正 AI 都查过了\"。",
          "",
          "还有一个藏在所有死法下面的共同点：**评审错位——评的东西不是关键的东西。** 把精力花在格式和\"你为什么不判空\"上，真正值钱的设计取舍、边界情况、长期可维护性，反而没人聊。下面每条经验，都是把评审拉回正轨的做法，一条条来自不同团队的实测，不是纸面理论。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：评审是减速带，不是检查站——前置、小步、对码不对人",
          "",
          "**把评审从\"写完代码之后的检查站\"改成\"分散在设计、开发、提交阶段的一组减速带\"：强制你在关键节点稍微慢一点，但不会让整个流程停下来等。** 赶时间只看这一条：PR 拆小（≤500 行）、评审前置（设计阶段先对齐）、评论对代码不对人。下面经验 1~5 是具体做法和为什么。",
          "",
          "### 经验 1：PR 拆小——小 PR 是评审质量的物理前提",
          "",
          "现象：大 MR 是评审失效的头号原因。2000 行 vs 200 行，评审难度 30 倍；双人评审比单人评审多消耗约 60% 的总时间，但额外发现的问题只有约 15%——不是评审人不行，是 MR 太大时，责任分散效应直接拉满：第一个人认真看完，第二个看到已有评论就\"大体认可\"，第三个只看个大概。这不是多人评审，这是责任分散的完美复现。",
          "",
          "做法：给 MR 立硬门禁——单次变更不超过 500 行（更保守的团队用 400 行甚至 200 行），超过先拆。拆不动？\"这次改动就是这么大，没法拆\"是伪命题——拆不动说明设计抽象不够，先做架构调整再动手。提交 MR 前配一份自检清单（覆盖测试、调试代码残留、变更行数、合并冲突、数据库迁移脚本、接口文档），有团队靠它消灭了约 30% 的评审往返。",
          "",
          "为什么：小 PR 让\"认真看完\"成为可能，也让每次评审的上下文是完整的——评审人不需要记住上个月的背景才能判断这段代码对不对。而且小 PR 天然分散风险：一个 PR 出问题，回滚范围小，业务不慌。大 MR 的本质是\"把评审责任转嫁给同事的注意力\"，小 PR 是\"把注意力还给评审人\"。",
          "",
          "### 经验 2：治形式主义——把\"义务\"改成\"质量\"，用机制消灭 LGTM",
          "",
          "现象：形式主义的核心原因就一句话——评审没有激励，只有义务。为了完成 KPI 打 LGTM、不看代码直接点确认，是每个团队都见过的场景。骂人是没用的，人不会因为被骂就变认真，但会因为有机制而改变行为。",
          "",
          "做法：三个被验证过的动作——①取消\"Review 通过率\"这类数量指标，改评\"评论质量\"：至少提一条与业务逻辑或架构相关的意见，否则算无效评审；②引入\"随机结对 + 命名匿名\"：评审人不知道自己评的是谁，避免人情分和熟人互捧；③设\"评审沙箱\"：每两周选一个高风险 PR，全员匿名评审打分，最佳评审者有小奖励。",
          "",
          "某团队实测：三个月后，有效评论占比从 35% 提升到 78%，线上缺陷率下降了 60%（从每万行代码 2.1 个降到 0.8 个）。",
          "",
          "为什么：人只对\"被衡量的事\"负责。把指标从\"数量\"换成\"质量\"，认真评审就变成了绩效的一部分而不是额外奉献；匿名化切断了人情干扰；沙箱制造了\"安全的练习场\"——练习发生在真实 PR 上之前。注意前提：这套动作适合 10 人以上的团队，小团队需要更柔性的做法（见 L3）。",
          "",
          "### 经验 3：别对人下手，对代码下手——评论是门手艺",
          "",
          "现象：CR 最容易翻车的地方，就一个字：\"你\"。你这里写错了。你为什么不判空。你这逻辑不太行。只要用了\"你\"，对方心里的防御盾就竖起来了——你不是在评审代码，你是在审判同事。我当年把同事批到绕着我走，就是栽在这上面。",
          "",
          "做法：把评论对象从\"人\"换成\"代码/场景/我们\"：不说\"你写错了\"，说\"这段逻辑在并发高的时候可能有风险\"；不说\"你命名不行\"，说\"这个 list1 里装的主要是什么数据？要不要换个更具体的名字，比如 activeUserList？\"；发现看不懂，先承认自己：\"这块我读了两遍才理顺，要不要用提前 return 拆一下？\"——先承认自己读得费劲，再给建议，用商量的语气收尾。",
          "",
          "格式问题交给工具：lint/prettier/AI 能解决 80% 的低级争吵，别在 PR 里留 20 条格式评论。",
          "",
          "真要重写也别在评论区硬刚——人多的地方没人愿意认输，私聊、语音、当面，把风险讲清楚，共识达成了，让他自己改。",
          "",
          "为什么：评审的本质是\"让别人愿意把问题改掉\"，不是\"证明我比你强\"。敌人是 Bug，不是同事。把对象从\"你\"换成\"这段代码\"，意思一点没变，攻击性直接砍半；承认自己读得费劲，是给双方台阶——对方大概率自己就发现问题了，你连补刀都不用。这条经验看起来\"软\"，其实是最硬的技术：它决定了前四条经验能不能持续运转——没人愿意给\"审判官\"看代码，但大家都愿意让\"兜底的人\"帮自己把关。",
          "",
          "### 经验 4：AI 时代，评审从\"批改作业\"变成\"补上下文\"",
          "",
          "现象：以前评审像老师改卷子——\"这里该 early return\"\"命名应该是 camelCase\"\"类型定义放 *.interface.ts\"。现在这些 Linter、Formatter、AI 全都能自动查，人一秒钟都不用花。很多团队就此躺平：\"反正 AI 都查过了\"，评审变成了摆设。但恰恰相反：AI 替人类干掉了低级检查，人类评审反而被解放出来干真正值钱的事——而大部分团队没接住这个红利。",
          "",
          "做法：把评审重心从\"对不对\"移到\"为什么\"和\"以后怎么办\"：不问\"这段代码对不对\"，问\"为什么选这个设计？这个组件的通用化粒度，以后加功能时扛得住吗？这个函数责任范围是不是太广？三个月后回来看这段代码，会不会皱眉？测试写起来费劲吗？\" 对 AI 生成的代码尤其要补上下文——AI 的代码最大的缺口就是\"没有为什么\"，所以评审人要追问：\"前提条件是什么？这个边界场景考虑了吗？",
          "",
          "\" 有个团队的做法很有代表性：从\"直接给答案\"改成\"一起想\"——\"这样写可以，但要不要考虑一下这个场景？",
          "",
          "\"——把评审从单向批改变成双向思考。",
          "",
          "为什么：机器能查\"写对了没有\"，但查不了\"这个设计对不对\"\"半年后维护它的人会不会想骂人\"\"这个决策和别的模块的时间线一致吗\"。这些判断需要业务上下文和长期视野，恰恰是人类唯一不可替代的部分。AI 时代评审的岗位不是消失了，是升级了：从\"语法检查员\"升级成\"上下文补充员\"。谁先完成这个转型，谁就同时拥有了 AI 的速度和人类的判断。",
          "",
          "### 经验 5：评审要度量，防膨胀——数据驱动，而不是追责",
          "",
          "现象：评审流程改了半年，效果好不好？大多数团队答不上来——没有度量，只有感觉。另一个极端是流程膨胀：每个改动都要三层评审、五个签字，每多一层评审就多一个等待点，每个等待点都可能变成阻塞点——评审从减速带变成了收费站。",
          "",
          "做法：建三个最小度量：平均评审时长、首次响应时长、评审发现的缺陷率（评审中拦下的问题数 / 上线后暴露的问题数）。用数据回答\"评审到底值不值\"，而不是靠感觉和争吵。定期审查评审流程本身：这层评审还在产出价值吗？这个等待点是不是已经变成阻塞点？是就拆掉。评审层级宁可少不可多——少一层只是多一份风险，多一层就是多一个永久等待点。",
          "",
          "为什么：度量让评审从\"信仰问题\"变成\"工程问题\"——有数据就能迭代，没数据就只能吵。而\"防膨胀\"是大型团队最该警惕的：评审的价值在于\"拦住问题\"，不在于\"流程看起来严格\"。数据驱动的团队把评审当成系统来调优，而不是当成制度来供奉。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 立 PR 门禁：单次变更 ≤500 行，配自检清单（测试/调试残留/行数/冲突/迁移/文档），超过先拆；",
          "2. 评审前置：设计阶段先对齐方案，别等代码写完再\"开喷\"；",
          "3. 改质量指标：取消通过率，评\"至少一条业务/架构相关意见\"，匿名随机结对（10 人以上团队）；",
          "4. 评论话术：对代码不对人，\"我们/这段代码\"代替\"你\"，格式交给工具，重写私聊；",
          "5. 建三个度量：评审时长、首次响应、缺陷率，每季度审视一次评审流程本身有没有膨胀。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目 / 开源项目**：没有同事，你的评审人是\"未来的自己\"——PR 拆小、写清\"为什么\"（提交信息就是你的评审记录）；给贡献者的评论，先感谢再提问，别让新人第一次提 PR 就被\"你\"字劝退。",
          "- **小团队（3-10 人）**：不要全套上匿名和沙箱——柔性做法：结对评审（写的人 + 看的人坐一起过一遍），话术先行，先让小 PR 和\"评审前置\"落地，这两条就能解决 80% 的痛。",
          "- **10 人以上团队**：经验 2 的三件套（质量评分、匿名结对、评审沙箱）可以上，配度量体系；最该警惕的是流程膨胀——每层评审都要能回答\"它还在产出价值吗\"。",
          "- **AI 时代**：把表面检查全部交给工具和 AI，人类评审只聊\"为什么\"和\"以后怎么办\"；对 AI 生成的代码，重点补\"前提条件、边界场景、设计取舍\"——AI 的代码最缺上下文，而上下文只有人能给。",
          "- **如果你正在\"评审变形式主义\"的团队里**：别先从别人下手，先从自己下手——下一次评审，提一条业务逻辑或架构相关的实质意见，把\"你\"换成\"这段代码\"，你会看到评论区的气氛变掉。机制是团队的，话术是自己的，而话术是你能立刻开始的那一半。",
          "",
          "收尾：最后说句实话：我见过最伤人的评审，不是挑错挑得狠，而是从头到尾没人在乎「为什么」。代码总会写错，评审的意义从来不是抓住错误，是让写代码的人和看代码的人，对「这段代码为什么存在」达成一致。下一次评审，别急着挑毛病——先问一句「这里为什么这么写」，你会看到评论区从战场变成课堂。AI 时代尤其如此：表面检查 AI 全包了，人类只剩「为什么」这一件事值得做。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/code-review-consensus",
        "agent": "https://trove.xin/agent/problems/code-review-consensus"
      }
    },
    {
      "id": "code-review-graph-2",
      "title": "名称混淆：juspay/code-review-graph-rescript 同名变体",
      "domain": "review",
      "domain_label": "代码审查",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "code-review-graph"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你搜 \"code-review-graph\"，结果里混着一个 `juspay/code-review-graph-rescript`，教程里的安装命令、仓库地址全对不上，装完发现根本不是你要的东西。同名项目把你带沟里了。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "github 上存在一个**同名前缀的 rescript 生态项目**（juspay 组织），和我们要装的 `code-review-graph` 没有任何关系。两个项目都叫类似的名字，搜索引擎很容易把国外信号（L32 Google 窗口期捕获）混进来。",
          "",
          "项目身份（identity）有三个独立的锚点：**仓库地址**（源码归属）、**分发包名**（安装寻址）、**命令面**（运行时行为）。搜索引擎和教程混的是“名字相似”，但真正决定“你装到的是什么”的是后两个锚点：pip 按包名寻址，命令按 PATH 里的可执行文件寻址。",
          "",
          "rescript 变体用 rescript 生态的安装命令，装出来的命令和 PyPI 包完全不同——所以“认 PyPI 包名 + 验 `--version` 输出”能在不依赖搜索引擎判断的情况下给出确定性结论。",
          "",
          "这也是为什么排查顺序固定：先 `pip install` 指定精确包名（锁定分发包锚点）→ 再 `--version` 验命令面（锁定行为锚点）→ 最后才看文档仓库（确认源码归属）。搜索引擎只是入口，不是证据。",
          "",
          "| 项目 | 仓库 | 语言生态 | 安装方式 |",
          "|---|---|---|---|",
          "| 我们要的 | tirth8205/code-review-graph | Python | `pip install code-review-graph` |",
          "| 混淆变体 | juspay/code-review-graph-rescript | Rescript | rescript 生态安装命令 |",
          "",
          "认准两个硬证据：**PyPI 包名**和 **`--version` 输出**。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：装完立刻验命令面，输出 2.3.8 才是对的（PyPI 包 code-review-graph）",
          "pip install code-review-graph==2.3.8 && code-review-graph --version",
          "```",
          "",
          "输出 `code-review-graph 2.3.8` 即正确目标；看到 rescript 相关内容就说明装错变体了。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 只认 PyPI 包名 — **必做**",
          "",
          "```bash",
          "pip install code-review-graph==2.3.8",
          "```",
          "",
          "包名 `code-review-graph`（PyPI），不要用 rescript 生态教程里的安装命令。",
          "",
          "### 2. 验命令面 — **必做**",
          "",
          "```bash",
          "code-review-graph --version",
          "```",
          "",
          "预期输出 `code-review-graph 2.3.8`（实测）。",
          "",
          "### 3. 查文档认准仓库 — **必做**",
          "",
          "仓库地址 `tirth8205/code-review-graph`（MIT 协议）。教程里出现 `juspay` 或 `rescript` 字样直接跳过。"
        ],
        "怎么确认修好了": [
          "",
          "版本命令输出符合预期（实测输出）：",
          "",
          "```text",
          "code-review-graph --version",
          "→ code-review-graph 2.3.8",
          "```",
          "",
          "`pip show code-review-graph` 能看到 Home-page 指向 tirth8205 仓库。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| pip / pipx | 包名一致；pipx 为官方推荐路径（本机未实测 pipx） |",
          "| 搜索引擎结果 | 国外信号易混入 rescript 变体，认 PyPI 包名最稳 |",
          "| Windows / macOS | 安装与版本命令相同 |",
          "| 已装错 | `pip uninstall` 卸载后按正确包名重装 |",
          ""
        ],
        "症状": [
          "",
          "",
          "搜索资料时把 code-review-graph 与 `juspay/code-review-graph-rescript` 混为一谈，装错仓库/命令对不上。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "juspay 的 rescript 生态同名项目（国外信号，L32 Google 窗口期捕获）；正确仓库 = `tirth8205/code-review-graph`（Python 包 `code-review-graph`，manifest 签名），两者无关联。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 按包名认项目：pip 装的是 `code-review-graph==2.3.8`（PyPI），命令 `code-review-graph --version` 输出 `code-review-graph 2.3.8`。",
          "2. 查文档认准仓库：tirth8205/code-review-graph（MIT），勿用 rescript 生态教程的安装命令。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28）：PyPI 包 code-review-graph 2.3.8 安装后命令面正常；混淆变体来源 = 站内文档（L32 国外信号）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/code-review-graph-2",
        "agent": "https://trove.xin/agent/problems/code-review-graph-2"
      }
    },
    {
      "id": "code-review-graph-3",
      "title": "安装后「我的配置被改了」/ 与 omc、agt 疑似冲突",
      "domain": "review",
      "domain_label": "代码审查",
      "category": "coexist-compat",
      "category_label": "组合与兼容",
      "applies_to": [
        "code-review-graph"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你执行 `code-review-graph install`（或 init）后，发现 `~/.claude.json`、`~/.codex/hooks.json` 等配置文件被写入了内容；如果还装了 oh-my-claudecode、agent-governance-toolkit，你开始怀疑它们互相打架。第一反应是\"中毒了/出 bug 了\"，其实这是这个工具的**正常行为**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`install` 子命令会向检测到的 AI 平台注册 MCP/hooks——这是它的**功能设计**，不是故障：",
          "",
          "| 写面 | 写入内容 |",
          "|---|---|",
          "| ~/.claude.json | mcpServers 段 |",
          "| ~/.codex/hooks.json | hooks 注册 |",
          "| ~/.cursor/mcp.json | MCP 注册 |",
          "| ~/.config/opencode/plugins/crg-plugin.ts | 插件注册 |",
          "| 工作区 .claude/settings.json / .mcp.json | 工作区级注册 |",
          "",
          "与 omc/agt 存在**同文件写面**（不同 key/段），判定为 possible-conflict（未实测共装，不冒充实测态）。全部写入都可由官方 `code-review-graph uninstall` 完整清理。",
          "",
          "AI 工具的“安装”本质是**注册**：往各客户端的共享配置文件里写入 MCP/hooks 注册段，让客户端启动时能发现并加载这个工具。`install` 就是执行这些写操作，所以“装完配置被改”不是异常，而是安装的定义本身。",
          "",
          "理解这点后有两个确定性推论：一是**写面是公开契约**——写到哪些文件、哪些段，写在 guide/manifest 里，装之前先看写面清单，就不会被吓到；二是**同文件写面 ≠ 冲突**——omc/agt 与 crg 写的是同一批文件但不同 key/段，就像两个程序各自写自己的配置项，只有写同一 key 才可能互相覆盖（这才是真冲突）。",
          "",
          "判断口径统一：看冲突矩阵里是 possible-conflict 还是实测冲突，不自己猜。回滚路径也因此固定：官方 `uninstall` 是写操作的逆操作，能完整撤销注册。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认这些写入是功能不是故障；不需要就完整清理",
          "code-review-graph uninstall",
          "```",
          "",
          "uninstall 是官方清理入口；执行后检查下面的残留清单即可。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 装前知悉写面 — **必做**",
          "",
          "install 注册 MCP 是功能不是故障。装之前就知道它会写这些文件，就不慌。",
          "",
          "### 2. 共装策略 — **必做**",
          "",
          "与 omc/agt 同环境：先装一个跑通再评估下一个。冲突口径见 站内文档（possible-conflict，未实测不冒充实测态）。",
          "",
          "### 3. 清理残留 — **必做**",
          "",
          "官方 uninstall 后手动确认清单：",
          "",
          "```bash",
          "ls -la ~/.claude.json ~/.codex/hooks.json ~/.cursor/mcp.json 2>/dev/null && ls -la .code-review-graph/",
          "```",
          "",
          "确认工作区 `.code-review-graph/` 与各配置文件中对应段已清掉。"
        ],
        "怎么确认修好了": [
          "",
          "uninstall 后配置文件不再含 code-review-graph 相关段（预期输出形态）：",
          "",
          "```text",
          "grep -c code-review-graph ~/.claude.json",
          "→ 0（或不含该段）",
          "```",
          "",
          "各 AI 平台配置中 mcpServers/hooks 恢复原样；其它工具不受影响。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 只装 crg | 写入 ~/.claude.json 等是正常注册行为 |",
          "| crg + omc/agt 共装 | 同文件写面不同 key/段，possible-conflict，未实测共装 |",
          "| 想回滚 | 官方 uninstall 完整清理；残留按清单人工确认 |",
          "| Windows / macOS | 配置文件路径对应各平台用户目录 |",
          ""
        ],
        "症状": [
          "",
          "",
          "执行 `code-review-graph install`（或 init）后，发现 ~/.claude.json、~/.codex/hooks.json 等配置文件被写入；或与 oh-my-claudecode、agent-governance-toolkit 共装时怀疑互相干扰。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "install 子命令会向检测到的 AI 平台注册 MCP/hooks：~/.claude.json（mcpServers）、~/.codex/hooks.json、~/.cursor/mcp.json、~/.config/opencode/plugins/crg-plugin.ts、工作区 .claude/settings.json 与 .mcp.json——这是该工具的正常行为（多注册面重件）。与 omc/agt 存在同文件写面（不同 key/段），判定 possible-conflict（未实测共装）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 装前知悉写面：install 注册 MCP 是功能不是故障；全部可由官方 `code-review-graph uninstall` 完整清理。",
          "2. 共装策略：与 omc/agt 同环境先装一个跑通再评估；冲突矩阵口径见 站内文档（possible-conflict，未实测不冒充实测态）。",
          "3. 清理残留：官方 uninstall + 手动确认清单（工作区 .code-review-graph/、~/.claude.json mcpServers 段、~/.codex/hooks.json、~/.cursor/mcp.json）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材引用：本市场 guide FAQ + manifest 共享面说明（写面清单逐项列出）；冲突判定来源 站内文档。",
          "- 本环境未执行 install 子命令（避免写真实配置）——如实标注；写面清单来自 guide/manifest 结构审计。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/code-review-graph-3",
        "agent": "https://trove.xin/agent/problems/code-review-graph-3"
      }
    },
    {
      "id": "code-review-graph-4",
      "title": "GitHub 直连慢/失败（依赖 GitHub 的场景）",
      "domain": "review",
      "domain_label": "代码审查",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "code-review-graph"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "在 `code-review-graph` 的构建/更新流程里，凡是碰到 GitHub 的操作（仓库元数据、上游资源）就卡住或失败。你会以为工具坏了，其实大部分场景**根本不依赖 GitHub**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- 本市场连通性基线（connectivity-20260828b）：GitHub 直连慢、raw 超时，国内出口不稳。",
          "- 这个工具的核心用法是**解析本地代码**（tree-sitter），数据存 SQLite（工作区 `.code-review-graph/`），build/update 不依赖网络。",
          "- 只有少数场景（仓库元数据、上游资源下载）才需要访问 GitHub。",
          "",
          "| 场景 | 是否依赖 GitHub |",
          "|---|---|",
          "| build / 图构建 | ❌ 本地代码 + tree-sitter，离线可用 |",
          "| update 本地分析 | ❌ 数据源是本地仓库 |",
          "| 下载类操作 | ✅ 需访问 GitHub，受出口影响 |",
          "",
          "判断“工具依赖什么”不能看表象（报错在 GitHub 相关环节），要看**数据流**：code-review-graph 的核心输入是本地代码（tree-sitter 解析），中间产物存在本地 SQLite，输出是本地图数据——整条链路里没有一步必须访问 GitHub，所以它本质是**离线工具**。会碰 GitHub 的只有“获取元数据/下载上游资源”这类**可选的补充环节**。",
          "",
          "这就是“先分清是哪一步卡”的方法论：卡住的位置决定根因——本地解析卡住（build 阶段）跟网络无关，检查路径与解析器；下载/元数据卡住才是连通性问题。由此也得出加速策略的分层：核心流程不碰网络（离线直接跑），pip 类安装走国内镜像（包分发层），GitHub 下载走 gh-proxy（内容层）——每一层都有自己的加速通道，别用错层。",
          "",
          "验证离线能力也有固定方法：断开外网重跑 build，能完成即证明核心不依赖 GitHub。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：核心流程不碰 GitHub，离线直接跑；确需联网再走加速通道",
          "code-review-graph build --repo . && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph",
          "```",
          "",
          "build 解析本地代码不联网；pip 安装走清华镜像（本市场实测可用）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先分清是哪一步卡 — **必做**",
          "",
          "build/update 本地分析卡住 = 不是 GitHub 问题，检查代码仓库路径与 tree-sitter 解析。下载/元数据类操作卡住才是 GitHub 连通性。",
          "",
          "### 2. 核心用法离线跑 — **必做**",
          "",
          "```bash",
          "code-review-graph build --repo .",
          "```",
          "",
          "数据落在工作区 `.code-review-graph/`（SQLite），全程不访问 GitHub。",
          "",
          "### 3. 确需联网时走加速 — **必做**",
          "",
          "GitHub 下载走 gh-proxy 等加速通道或错峰重试；pip 类安装固定走清华镜像：",
          "",
          "```bash",
          "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph",
          "```",
          "",
          "### 4. 验证本地可离线 — **必做**",
          "",
          "断开外网后重复 build，能完成即证明核心流程不依赖 GitHub。"
        ],
        "怎么确认修好了": [
          "",
          "build 流程完成且产物落盘（预期形态）：",
          "",
          "```text",
          "code-review-graph build --repo .",
          "→ build 完成日志 + 工作区 .code-review-graph/ 下生成图数据文件",
          "```",
          "",
          "安装全程走清华镜像成功（本市场实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 完全离线 | 核心 build/update 可用；下载类功能不可用 |",
          "| GitHub 慢/raw 超时 | 走 gh-proxy 等加速通道或错峰重试 |",
          "| pip 安装 | 清华镜像实测可用 |",
          "| 平台接入 | 真实 codex 平台接入需平台账号，未实测 |",
          "| 未实测项 | Windows/macOS、pipx 路径、完整 build 全流程（需目标仓库与较长执行时间） |",
          ""
        ],
        "症状": [
          "",
          "",
          "`code-review-graph` 构建/更新流程中访问 GitHub（如仓库元数据、上游资源）卡住或失败。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "本市场连通性基线：GitHub 直连慢、raw 超时（connectivity-20260828b）；工具本身主要数据源是本地仓库，GitHub 依赖场景较少，但国内出口不稳时仍会受影响。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 核心用法不依赖 GitHub：build/update 解析本地代码（tree-sitter，SQLite 存工作区 .code-review-graph/），离线可用。",
          "2. 确需访问 GitHub 时走加速通道（gh-proxy 等）或错峰重试；下载类操作见本市场各条目的连通性情报。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 连通性基线 connectivity-20260828b（GitHub 直连慢、PyPI 清华镜像可用）；本市场实测安装全程走清华镜像成功。",
          ""
        ],
        "未能实测项（如实标注）": [
          "",
          "- 真实 codex 平台接入（需平台账号）；install/init 子命令的配置写面（本环境未执行，避免污染真实配置）；完整 build/图构建全流程（需目标仓库与较长执行时间）；Windows/macOS 环境；pipx 安装路径（官方推荐，本机未实测）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/code-review-graph-4",
        "agent": "https://trove.xin/agent/problems/code-review-graph-4"
      }
    },
    {
      "id": "codex-cli-chatgpt-not-found",
      "title": "ChatGPT Windows 客户端打不开：Unable to locate the Codex CLI binary / 找不到 Codex CLI",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "codex"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://blog.51cto.com/kaigejava/14886935",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "更新 ChatGPT Windows 客户端后双击打不开，直接弹错误：",
          "",
          "```text",
          "ChatGPT failed to start.",
          "Unable to locate the Codex CLI binary.",
          "Set CODEX_CLI_PATH or ensure the client resources include bin/codex.",
          "```",
          "",
          "打开的是 ChatGPT，报的却是 Codex CLI 找不到——新版 ChatGPT 客户端启动时依赖 Codex CLI 二进制。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "客户端启动时要定位 `codex` 二进制：先找 PATH，再找 `CODEX_CLI_PATH` 指向的路径，都没有就拒绝启动。常见三种情况：",
          "",
          "| 情况 | 说明 |",
          "|---|---|",
          "| 没装 Codex CLI | `codex --version` 报「不是内部或外部命令」 |",
          "| 装了但不在 PATH | 安装后未刷新 PATH / PATH 被改 |",
          "| `CODEX_CLI_PATH` 指向错误 | 变量名写错、路径照抄他人（不同机器安装布局不同） |",
          "",
          "客户端定位 CLI 二进制是「PATH → 环境变量覆盖」的两级寻址：先在 PATH 里按名字找 `codex`，找不到再看 `CODEX_CLI_PATH` 指向的具体路径，两级都落空才拒绝启动。",
          "",
          "这个顺序决定了三种失败的语义不同：PATH 没有 = 没装或没刷新 PATH；PATH 有但 CODEX_CLI_PATH 指错 = 变量覆盖了正确寻址（变量优先于 PATH，指向不存在就整体失败）；变量名写错 = 等于没设，退回 PATH 查找。",
          "",
          "确定性推论：报「找不到 codex」先 `codex --version` 直接验证二进制在不在，再 `echo $CODEX_CLI_PATH` 看变量是否残留；配置里写死路径前先确认 `which codex` 的输出与机器布局一致（别人的路径不能照抄）；修复顺序：装/刷 PATH 解决 ①②，`unset CODEX_CLI_PATH` 或改正路径解决 ③。",
          ""
        ],
        "快速修复": [
          "",
          "```powershell",
          "# 1. 检查是否已装（返回版本号 = 已装）",
          "codex --version",
          "",
          "# 2. 没装：用官方 PowerShell 安装脚本（需能访问 chatgpt.com）",
          "powershell -ExecutionPolicy ByPass -c \"irm https://chatgpt.com/codex/install.ps1 | iex\"",
          "",
          "# 3. 以真实路径为准设置 CODEX_CLI_PATH（不要照抄网上的完整路径）",
          "where codex",
          "setx CODEX_CLI_PATH \"<where codex 返回的完整路径>\"",
          "",
          "# 4. 完全退出 ChatGPT 客户端（不是最小化）再重开",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 检查 Codex CLI 是否存在 — **排查**",
          "",
          "```powershell",
          "codex --version",
          "```",
          "",
          "`'codex' 不是内部或外部命令` → 没装或不在 PATH，进第 2 步；返回版本号 → 已装，进第 3 步。",
          "",
          "### 2. 安装 Codex CLI — **必做**",
          "",
          "```powershell",
          "powershell -ExecutionPolicy ByPass -c \"irm https://chatgpt.com/codex/install.ps1 | iex\"",
          "```",
          "",
          "装完再 `codex --version` 确认。",
          "",
          "### 3. 配置 CODEX_CLI_PATH（按本机实际路径） — **必做**",
          "",
          "```powershell",
          "where codex",
          "setx CODEX_CLI_PATH \"C:\\Users\\<你的用户名>\\...\\codex.exe\"   # 以 where codex 输出为准",
          "```",
          "",
          "路径不要照抄：不同机器/不同安装器版本布局不同（文章默认是 `%LOCALAPPDATA%\\Programs\\OpenAI\\Codex\\bin\\codex.exe`，本机实测是 `%LOCALAPPDATA%\\OpenAI\\Codex\\bin\\<hash>\\codex.exe`）。变量名必须是 `CODEX_CLI_PATH`，别写错。",
          "",
          "### 4. 重开客户端 — **必做**",
          "",
          "环境变量只对新进程生效：**完全退出** ChatGPT 客户端（托盘/任务管理器确认无残留）再启动。"
        ],
        "怎么确认修好了": [
          "",
          "`codex --version` 正常返回版本号；ChatGPT 客户端能正常打开、不再弹 Unable to locate the Codex CLI binary。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| codex 已在 PATH、未设 CODEX_CLI_PATH | 客户端同样正常（本机实测：0.151.0-alpha.7.1 在 PATH，CODEX_CLI_PATH 未设，客户端正常启动）——PATH 满足即可，CODEX_CLI_PATH 是显式兜底 |",
          "| 安装脚本 | https://chatgpt.com/codex/install.ps1 校园网直连超时（http=000），需网络窗口/代理，本机未实测 |",
          "| 未实测 | 完整「客户端打不开」场景本机未复现（本机客户端正常），修复步骤来自原文实测 |",
          ""
        ],
        "症状": [
          "",
          "ChatGPT Windows 客户端打不开：`ChatGPT failed to start. Unable to locate the Codex CLI binary. Set CODEX_CLI_PATH or ensure the client resources include bin/codex.`；`codex --version` 可能报「不是内部或外部命令」。",
          ""
        ],
        "原因": [
          "",
          "新版 ChatGPT 客户端启动依赖 Codex CLI 二进制：PATH 里没有 codex，且 `CODEX_CLI_PATH` 未设置或指向错误 → 客户端拒绝启动。",
          ""
        ],
        "解法": [
          "",
          "1. `codex --version` 确认是否已装；",
          "2. 未装 → 官方脚本安装：`powershell -ExecutionPolicy ByPass -c \"irm https://chatgpt.com/codex/install.ps1 | iex\"`；",
          "3. `where codex` 拿真实路径，`setx CODEX_CLI_PATH \"<真实路径>\"`（变量名别写错、路径别照抄）；",
          "4. 完全退出客户端（非最小化）再重开。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机验证（2026-08-30，Windows 11）：codex-cli 0.151.0-alpha.7.1 在 PATH（`where codex` → `%LOCALAPPDATA%\\OpenAI\\Codex\\bin\\<hash>\\codex.exe`），CODEX_CLI_PATH 未设但客户端正常启动 → PATH 满足即可、CODEX_CLI_PATH 是显式兜底；实际安装布局与文章默认路径不同（以 where codex 为准）。证据：实测复现日志（2026-08-30）。",
          "- 未实测：安装脚本（chatgpt.com 校园网直连超时 http=000，需网络窗口）；「客户端打不开」完整场景（本机未复现，步骤来自原文）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/codex-cli-chatgpt-not-found",
        "agent": "https://trove.xin/agent/problems/codex-cli-chatgpt-not-found"
      }
    },
    {
      "id": "codex-sandbox-setup-error",
      "title": "Codex 沙盒报错 helper_unknown_error: setup refresh had errors（写权限/ACL）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "codex"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/grey-wolf/p/22743305",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Codex 执行 shell 时突然报 `helper_unknown_error: setup refresh had errors`；就算给了 `workspace_write` 权限，写项目目录下的文件也反复失败；失败后 agent 只能尝试提权方式执行（需要人工审批）。新电脑 + 从旧电脑拷贝的目录、跨机器/跨用户共享目录、WSL UNC 路径最容易触发。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Codex 启动时会跑一次「setup refresh」：对配置的 write roots 做 **ACL/ACE 检查并补写权限**，失败则整体报 `setup refresh had errors`（界面显示 helper_unknown_error）。典型触发：",
          "",
          "| 触发场景 | 机制 |",
          "|---|---|",
          "| 目录 owner 是另一台机器/已删除用户的 SID | 当前用户不是 owner，无权修改目录 ACL → ACE 检查失败 |",
          "| 旧电脑直接拷贝的文件夹 | owner SID 不匹配，同上 |",
          "| UNC/WSL 路径（`\\\\wsl.localhost\\...`） | `GetSecurityInfo` 失败（本机实测变体，错误码 1） |",
          "",
          "启动时的 setup refresh 是对 write roots 的 **ACL/ACE 权限自检与补写**：客户端要确认当前用户对配置的写目录有权限，检查失败就整体报 `setup refresh had errors`。",
          "",
          "Windows 的 ACL 按 SID 记录属主：目录 owner 若是另一台机器或已删除用户的 SID，当前用户不是 owner，即使有读写权限也无法修改 ACL（修改 ACL 需要 owner 或特权）——「拷贝过来的文件夹」是最典型来源。",
          "",
          "UNC/WSL 路径（`\\wsl.localhost\\...`）在 `GetSecurityInfo` 层失败是另一条独立链路（本机实测变体）。",
          "",
          "确定性推论：先分清「权限不足」与「ACL 不可改」——`icacls <目录>` 看 owner 与实际 ACL；拷来的目录用 `takeown /f <目录> /r` 拿回属权再改 ACL；UNC/WSL 路径的写 root 换成 WSL 内路径（`/root/...`）而不是 Windows 侧路径；判断路径：报错在权限检查阶段 + 目录来自拷贝 = SID 属主问题，先 takeown。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：改 owner 为当前用户（最常见修复），失败再补 ACL",
          "takeown /f <目录> /r /d y",
          "```",
          "",
          "管理员权限的 PowerShell/Cmd 里执行；目录换成报错日志里的实际路径。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看沙盒日志定位 — **排查**",
          "",
          "比瞎猜快：`C:\\Users\\<user>\\.codex\\.sandbox\\sandbox.<date>.log`（旧版路径 `C:\\Users\\<user>\\.codex.sandbox\\...`）。搜 `setup refresh had errors` / `write ACE`，看具体是哪个路径、什么错误。",
          "",
          "### 2. 改 owner（最常见修复） — **必做**",
          "",
          "命令行等价：",
          "",
          "```bash",
          "takeown /f <dir> /r /d y",
          "```",
          "",
          "或图形界面：资源管理器 → 目录属性 → 安全 → 高级 → 更改所有者 → 当前用户，勾选「替换子容器和对象的所有者」。",
          "",
          "### 3. 补 ACL（owner 正确但仍缺权限时） — **必做**",
          "",
          "```bash",
          "icacls <dir> /grant <user>:(OI)(CI)F /T",
          "```",
          "",
          "### 4. 恢复验证 — **必做**",
          "",
          "重新开一个 Codex 会话触发 setup refresh，日志出现 `errors=[]` 即恢复——幂等，不需要重装/重置。"
        ],
        "怎么确认修好了": [
          "",
          "沙盒日志出现恢复标记（实测输出形态）：",
          "",
          "```text",
          "sandbox.<date>.log",
          "→ setup refresh completed with errors: []（errors=[]）",
          "→ setup binary completed",
          "```",
          "",
          "Codex 会话里对项目目录的写操作正常，不再需要提权审批。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 旧电脑拷贝的目录 | owner SID 不匹配，takeown 改 owner 即解 |",
          "| UNC/WSL 路径 | `GetSecurityInfo failed ...: 1`（本机实测变体），同样走 owner/ACL 修复 |",
          "| 只缺写权限 | `icacls /grant <user>:(OI)(CI)F /T` 即可 |",
          "| 恢复 | 幂等，新会话 setup refresh 自动通过，无需重装 |",
          "| 未实测项 | 非 Windows 平台行为未实测 |",
          ""
        ],
        "症状": [
          "",
          "Codex 执行 shell 时报 `helper_unknown_error: setup refresh had errors`；即使用 `workspace_write` 权限，写项目目录下的文件也反复失败；失败后 agent 尝试提权方式执行（需要人工审批），同意后才能继续。常见于新电脑 + 从旧电脑拷贝的目录、或跨机器/跨用户共享目录、WSL UNC 路径。",
          ""
        ],
        "原因": [
          "",
          "Codex 启动时会跑一次「setup refresh」：对配置的 write roots 做 ACL/ACE 检查并补写权限，失败则整体报 `setup refresh had errors`（界面显示 helper_unknown_error）。典型触发：",
          "",
          "1. 目录 owner 是另一台机器/已删除用户的 SID（旧电脑直接拷贝的文件夹）——当前用户不是 owner，无权修改该目录 ACL → ACE 检查失败；",
          "2. UNC/WSL 路径（`\\\\wsl.localhost\\...`）上 `GetSecurityInfo` 失败（本机实测变体，错误码 1）。",
          ""
        ],
        "解法": [
          "",
          "1. **先看沙盒日志定位**（比瞎猜快）：`C:\\Users\\<user>\\.codex\\.sandbox\\sandbox.<date>.log`（旧版路径 `C:\\Users\\<user>\\.codex.sandbox\\sandbox.<date>.log`）。搜 `setup refresh had errors` / `write ACE`，能看到具体是哪个路径、什么错误。",
          "2. **改 owner（最常见修复）**：资源管理器 → 目录属性 → 安全 → 高级 → 更改所有者 → 当前用户，勾选「替换子容器和对象的所有者」；命令行等价：`takeown /f <dir> /r /d y`。",
          "3. **补 ACL（owner 正确但仍缺权限时）**：`icacls <dir> /grant <user>:(OI)(CI)F /T`。",
          "4. **恢复验证**：重新开一个 Codex 会话触发 setup refresh，日志出现 `errors=[]` 即恢复——幂等，不需要重装/重置。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地日志复现（2026-08-29）：本机 `C:\\Users\\<user>\\.codex\\.sandbox\\sandbox.<date>.log` 完整记录报错链：`write ACE check failed on \\\\?\\UNC\\wsl.localhost\\Ubuntu\\root\\<repo>: GetSecurityInfo failed ...: 1` → `setup refresh completed with errors` → `setup error: setup refresh had errors`（即 helper_unknown_error 底层）。",
          "- 恢复验证：同日 18:20 日志 `errors=[]`、`setup binary completed` —— ACL 问题解决后自动恢复，幂等。",
          "- 机制验证（Windows 侧 icacls 实测，临时目录）：`icacls ... /deny <user>:(W)` 后写入 → Access denied（与沙盒写失败同机制）；`/remove:d` 后写入 → 成功；`takeown /f` 修改 owner 命令可用。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/codex-sandbox-setup-error",
        "agent": "https://trove.xin/agent/problems/codex-sandbox-setup-error"
      }
    },
    {
      "id": "codex-update-msix",
      "title": "Codex 桌面应用更新失败（应用内更新报错 / MSIX 手动安装）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "codex"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/grey-wolf/p/22749811",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Codex 桌面应用的版本明显偏旧，想更新时：应用内点「更新」直接报错（常见于这台电脑改过注册表、禁用了各种自动更新）；从 Microsoft Store 或官网下载的 `ChatGPT Installer.exe` 双击提示「已经安装了」，没有更新动作；下载官方 MSIX 包双击后点了更新，也可能长时间没反应。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 环节 | 机制 |",
          "|---|---|",
          "| 应用内更新报错 | Windows 自动更新被注册表策略禁用时，Codex 自更新通道不可用 |",
          "| Installer.exe 提示已安装 | MSIX 应用已注册，安装器检测到旧版存在但不会静默覆盖 |",
          "| 双击 MSIX 无反应 | 包安装过程没有可见进度；旧版进程未退出时安装会失败（报「正在运行/没有关」） |",
          "",
          "核心修复路径是绕过 Store/安装器，直接用 PowerShell 的 `Add-AppxPackage` 安装官方 MSIX 包。",
          "",
          "Codex 桌面端是 MSIX 打包应用，更新走「应用内更新通道」：Windows 更新策略（注册表）禁用自动更新时，通道不可用——这不是应用 bug，是系统策略层的拒绝。MSIX 的核心语义是「按包注册」：应用已注册时，安装器检测到旧版本存在但不静默覆盖（需要先注销/退出）；双击 MSIX 无反应是因为安装过程没有可见进度，且旧版进程未退出时新包安装失败。",
          "",
          "确定性推论：更新失败先查系统更新策略（`reg query` 相关策略键）与应用是否在运行；绕过 Store/安装器的正路是 PowerShell `Add-AppxPackage` 直接装官方 MSIX 包（`-ForceApplicationShutdown` 处理占用进程）；「双击没反应」= 看事件日志或改用命令行装，别反复双击；",
          "",
          "判断路径：策略被禁用 = 走命令行通道，进程占用 = 先退出再装。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1. 先关掉所有 Codex/ChatGPT 进程（不关必失败）",
          "Get-Process ChatGPT,Codex | Stop-Process -Force",
          "",
          "# 2. 安装官方 MSIX 包（路径换成你的下载位置）",
          "Add-AppxPackage -Path C:\\Users\\<你>\\Downloads\\ChatGPT-x64.msix",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 下载官方 MSIX（认准官网域名） — **必做**",
          "",
          "- x64：`https://persistent.oaistatic.com/codex-app-prod/ChatGPT-x64.msix`（约 780MB）",
          "- arm64：`https://persistent.oaistatic.com/codex-app-prod/ChatGPT-arm64.msix`",
          "- 官网入口核对：`https://learn.chatgpt.com/docs/enterprise/windows-deployment`——这两个链接指向最新包，每天下载内容可能不同",
          "",
          "### 2. 确认没有旧版进程在跑 — **必做**",
          "",
          "```powershell",
          "Get-Process ChatGPT,Codex",
          "```",
          "",
          "有输出就先关：`Get-Process ChatGPT,Codex | Stop-Process -Force`。本机实测：Codex 桌面应用有 12 个 ChatGPT 进程 + 1 个 codex CLI 进程，不关掉直接安装会失败。",
          "",
          "### 3. 命令行安装 — **必做**",
          "",
          "```powershell",
          "Add-AppxPackage -Path C:\\Users\\<你>\\Downloads\\ChatGPT-x64.msix",
          "```",
          "",
          "执行时会有进度条；失败会给出原因（例如旧版进程未关闭）。",
          "",
          "### 4. 验证 — **必做**",
          "",
          "```powershell",
          "Get-AppxPackage *ChatGPT* | Select-Object Name, Version",
          "```",
          "",
          "版本号变为最新即成功；应用内 About 也显示新版本。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "Get-AppxPackage *ChatGPT* | Select-Object Name, Version",
          "```",
          "",
          "Version 等于最新版；启动 Codex 后关于页显示新版本号，应用内「更新」不再报错。命令面已在本机验证（Add-AppxPackage 可用、进程诊断与文章场景一致）；完整安装流程因会改动本机应用未实测。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| x64 / arm64 | 两个 MSIX 包二选一，按 CPU 架构下载 |",
          "| 自动更新被禁用 | 应用内更新不可用，必须走 MSIX 手动安装 |",
          "| 老版本未退出 | Add-AppxPackage 直接报失败（提示先关闭），关进程后重试即可 |",
          "| 未实测项 | 完整 MSIX 覆盖安装流程（本机未执行，避免改动运行中的 Codex） |",
          ""
        ],
        "症状": [
          "",
          "Codex 桌面应用版本旧，应用内更新报错（Windows 自动更新被禁用时）；官网/Store 下载的 ChatGPT Installer.exe 提示已安装但不更新；MSIX 双击安装无反应或失败。",
          ""
        ],
        "原因": [
          "",
          "Windows 注册表策略禁用了自动更新，导致 Codex 应用内更新通道不可用；MSIX 应用已注册时 Installer.exe 不静默覆盖；MSIX 包安装需要 `Add-AppxPackage` 且要求旧版进程完全退出。",
          ""
        ],
        "解法": [
          "",
          "1. 下载官方 MSIX 包（persistent.oaistatic.com/codex-app-prod/ChatGPT-x64.msix 或 arm64，官网 learn.chatgpt.com/docs/enterprise/windows-deployment 可核对）；",
          "2. `Get-Process ChatGPT,Codex` 检查进程，`Stop-Process -Force` 全部关闭；",
          "3. `Add-AppxPackage -Path <msix>` 命令行安装，观察进度条与错误信息；",
          "4. `Get-AppxPackage *ChatGPT*` 验证版本号。",
          ""
        ],
        "验证证据": [
          "",
          "- 命令面实测（2026-08-30）：本机 `Get-Command Add-AppxPackage` → 可用（Source: Appx），MSIX 安装命令存在。",
          "- 进程诊断实测：`Get-Process ChatGPT,Codex` 返回 ChatGPT 12 个进程 + codex 1 个进程——与文章「旧版进程未关闭导致 Add-AppxPackage 失败」场景一致，诊断命令有效。",
          "- 官方地址核对：persistent.oaistatic.com/codex-app-prod/ChatGPT-x64.msix / arm64 与官网 learn.chatgpt.com/docs/enterprise/windows-deployment 一致（未下载，780MB）。",
          "- 未实测项：完整 MSIX 覆盖安装流程（会改动本机运行中的 Codex 应用，不执行）；实测复现日志（2026-08-30）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/codex-update-msix",
        "agent": "https://trove.xin/agent/problems/codex-update-msix"
      }
    },
    {
      "id": "crlf-command-not-found",
      "title": "bash 脚本报 $'\\r': command not found / bad interpreter: /bin/bash^M——Windows 换行符（CRLF）混入",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "encoding-format",
      "category_label": "编码与格式",
      "applies_to": [
        "bash"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/2920416/configure-bin-shm-bad-interpreter",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "在 WSL/Linux 上执行 Windows 里写好的脚本，报 `setup.sh: line 2: $'true\\r': command not found`，或 `bash: ./setup.sh: /bin/bash^M: bad interpreter`。报错里的 `\\r` / `^M` 是肉眼看不见的**换行残留**——用记事本/Windows 编辑器保存的脚本都会带上。",
          "",
          "有的脚本\"部分能跑、某几行突然 command not found\"，正是那些行尾带 CR 的命令。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Windows 文本文件用 CRLF（`\\r\\n`）结尾，Linux 只用 LF（`\\n`）：",
          "",
          "- **命令名后带 CR**：bash 把 `true\\r` 当成一个整体命令名 → `$'true\\r': command not found`（CR 在 echo 参数里通常不报错，所以\"时好时坏\"）；",
          "- **shebang 行带 CR**：`#!/bin/bash\\r` 让内核去找 `/bin/bash\\r`（^M）这个解释器 → `bad interpreter`。",
          "",
          "常见来源：记事本保存、Windows 编辑器默认 CRLF、git 检出配置不当（`core.autocrlf`）。",
          "",
          "> 实测环境：WSL Ubuntu（bash 5.3.9），2026-08-29。",
          "",
          "行尾的 CR（` `，0x0D）在 Linux 文本处理里是**普通字符**，不是行分隔符：bash 解析一行脚本时把整行（含末尾 ` `）当命令名，`true ` ≠ `true`，于是报 `$'true ': command not found`。",
          "",
          "shebang 是内核解析的：内核读第一行找解释器路径，`#!/bin/bash ` 让内核去找名为 `/bin/bash ` 的文件（不存在）→ `bad interpreter`。",
          "",
          "两个环节的失败点不同：命令名错误发生在 shell 层（报 command not found），shebang 错误发生在内核 exec 层（报 bad interpreter）。",
          "",
          "确定性推论：报错里带 `$'... '` 或 `^M` = CRLF 污染，`sed -i 's/ $//'` 或 `dos2unix` 一键清；git 检出后出现 = `core.autocrlf`/`.gitattributes` 行尾策略问题（见 git-crlf），修仓库配置而不是逐个文件改；",
          "",
          "判断路径：`file <脚本>` 看 CRLF，`cat -A` 看 `^M$`。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：去掉行尾 CR",
          "sed -i 's/\\r$//' setup.sh && bash setup.sh",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是不是 CRLF — **必做**",
          "",
          "```bash",
          "file setup.sh",
          "```",
          "",
          "输出带 `with CRLF` 字样 → 就是这个问题（或 `cat -A setup.sh` 看行尾是否 `^M$`）。",
          "",
          "### 2. 去 CR — **必做**",
          "",
          "```bash",
          "sed -i 's/\\r$//' setup.sh",
          "```",
          "",
          "或 `dos2unix setup.sh`（未预装时先 `sudo apt install dos2unix`）。",
          "",
          "### 3. 只有 shebang 行坏时 — **必做**",
          "",
          "`bash setup.sh` 显式调用解释器可绕过 shebang 解析（但正文 CR 仍在），根治还是去 CR。",
          "",
          "### 4. 预防 — **必做**",
          "",
          "- 在 Linux 内编辑脚本（vi/nano）；",
          "- Windows 编辑器选 LF/Unix 换行（VS Code 右下角改）；",
          "- git 仓库统一 `core.autocrlf` 策略；",
          "- 复制脚本后先 `file setup.sh` 看一眼。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "bash setup.sh",
          "```",
          "",
          "不再报 `command not found` / `bad interpreter` 即成功。实测：`sed -i 's/\\r$//'` 后脚本正常，exit=0；修复前正文 CR 报 `$'true\\r': command not found`（exit=127）、shebang CR 报 `bad interpreter`（exit=126）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| WSL/Linux 执行 | 一律去 CR：`sed -i 's/\\r$//'` 或 dos2unix |",
          "| Windows 编辑器保存 | 记事本默认 CRLF；VS Code 右下角切换 LF |",
          "| git 检出 | `core.autocrlf` 配置不当会重新引入 CRLF |",
          "| 部分命令报错 | 正是行尾带 CR 的命令，全量去 CR 即可 |",
          ""
        ],
        "症状": [
          "",
          "在 WSL/Linux 上执行 Windows 里写好的脚本，报出两类典型错误：",
          "",
          "```",
          "$ bash setup.sh",
          "setup.sh: line 2: $'true\\r': command not found",
          "",
          "$ ./setup.sh",
          "bash: ./setup.sh: /bin/bash^M: bad interpreter: No such file or directory",
          "```",
          "",
          "- 报错里的 `\\r`（或 `^M`）是肉眼看不到的换行残留——用记事本/Windows 编辑器保存的脚本会带上。",
          "- 有的脚本看起来「部分能跑」，某几行命令突然 `command not found`——正是那些行尾带 CR 的命令。",
          ""
        ],
        "原因": [
          "",
          "1. Windows 文本文件用 CRLF（\\r\\n）结尾，Linux 只用 LF（\\n）。",
          "2. **命令名后带 CR**：bash 把 `true\\r` 当成一个整体命令名 → `$'true\\r': command not found`（CR 在 echo 参数里通常不报错，所以「时好时坏」）。",
          "3. **shebang 行带 CR**：`#!/bin/bash\\r` 让内核去找 `/bin/bash\\r`（^M）这个解释器 → `bad interpreter`。",
          "4. 常见来源：记事本保存、Windows 编辑器默认 CRLF、git 检出配置不当（`core.autocrlf` 相关，见 git-crlf 条目）。",
          ""
        ],
        "解法": [
          "",
          "1. **快速修复（转换换行符）**：",
          "   ```",
          "   sed -i 's/\\r$//' setup.sh",
          "   # 或 dos2unix setup.sh",
          "   ```",
          "2. **如果只有 shebang 行坏**：`bash setup.sh` 显式调用解释器可绕过 shebang 解析（但正文 CR 仍在）；根治还是去 CR。",
          "3. **预防**：在 Linux 内编辑脚本（vi/nano）；Windows 编辑器选 LF/Unix 换行；git 仓库统一 `core.autocrlf` 策略（与 git-crlf 条目互补）；复制脚本后先 `file setup.sh` 看一眼（显示 `with CRLF` 即中招）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机（WSL Ubuntu 6.18.35.2，bash 5.3.9，2026-08-29）：",
          "- 实测 1（正文 CR）：`printf '#!/bin/bash\\ntrue\\r\\n'` + `bash` → `line 2: $'true\\r': command not found`，exit=127——与用户报错逐字一致。",
          "- 实测 2（shebang CR）：`#!/bin/bash\\r` + `chmod +x` + `./` 执行 → `/bin/bash^M: bad interpreter: No such file or directory`，exit=126。",
          "- 实测 3（修复）：`sed -i 's/\\r$//'` 后脚本正常，exit=0。",
          "- dos2unix 未安装（示例命令如实给出，未实测）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/crlf-command-not-found",
        "agent": "https://trove.xin/agent/problems/crlf-command-not-found"
      }
    },
    {
      "id": "crontab-env-path",
      "title": "crontab 任务报 command not found / 环境变量缺失（nvm、conda 装的东西跑不了）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "scheduling",
      "category_label": "调度与自动化",
      "applies_to": [
        "cron"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/21775749/command-not-found-even-crontab-path-is-set",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "crontab 定时任务跑起来后报找不到命令：交互终端里 `which node` 明明有结果，cron 任务却只留一行 `/bin/sh: 1: demo-node: not found`。或者任务能跑但读不到环境变量（NVM_DIR、CONDA_PREFIX、自定义 export）。日志只有一行，没更多线索。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "cron 执行任务的 shell 与我们交互终端的环境**完全不同**：",
          "",
          "| 差异点 | 说明 |",
          "|---|---|",
          "| 不加载 shell 配置 | cron 用 `/bin/sh` 执行，`~/.bashrc`、`~/.profile`、`/etc/profile` 全不加载——nvm/conda/pyenv 的初始化全部缺失 |",
          "| PATH 由 cron 守护进程决定 | 传统 SysV cron 默认 `/usr/bin:/bin`，不含自定义目录 |",
          "| systemd 场景（实测） | systemd 以 `cron -f -P` 启动时 PATH 含 `/usr/local/bin`，真正中招的是 `~/.nvm/versions/.../bin`、conda 环境目录这类\"只在 .bashrc 里加\"的路径和变量 |",
          "| alias | cron 环境里 alias 不生效 |",
          "",
          "cron 的执行环境与交互终端是**两套环境**：cron 用 `/bin/sh` 执行任务，不加载 `~/.bashrc`/`~/.profile`/`/etc/profile`——nvm/conda/pyenv 的初始化（只在 rc 里 export）全部缺失；",
          "",
          "PATH 由 cron 守护进程决定（传统 SysV cron 默认 `/usr/bin:/bin`，systemd 启动的 cron 稍宽但同样不含「只在 .bashrc 里加的路径」）。",
          "",
          "alias 在非交互 shell 里本来就不生效。",
          "",
          "确定性推论：cron 里「命令找不到/环境变量空」≠ 系统坏了，是「该命令不在 cron 的 PATH 里」；",
          "",
          "三条正路——脚本开头显式 `export PATH=...` 或写命令绝对路径（`/usr/bin/python3`）、把需要的初始化写进脚本本身（`source ~/.bashrc` 在 cron 里也可用但要看具体 shell）、或 cron 行里写 `bash -lc` 模拟登录环境（最接近终端，但引入终端环境的不确定性）；",
          "",
          "调试用 `* * * * * cmd >> /tmp/cron.log 2>&1` 看实际环境；",
          "",
          "判断路径：手动能跑、cron 不能 = 环境差异，不是脚本逻辑。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：crontab 顶部声明完整 PATH（最常用修复）",
          "# 第一行：PATH=~/.nvm/versions/node/v20/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
          "```",
          "",
          "按你的实际路径写；改完 `crontab 文件` 重载。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认任务的环境 — **必做**",
          "",
          "临时把 `printenv > /tmp/cron-env.txt 2>&1` 放进任务，跑一次看 PATH 和变量——比猜快得多。或直接看 cron 守护进程环境：",
          "",
          "```bash",
          "tr '\\0' '\\n' < /proc/$(pgrep -x cron)/environ | grep PATH",
          "```",
          "",
          "### 2. 修复 A：crontab 顶部声明 PATH — **必做**",
          "",
          "`PATH=...` 放在 crontab 第一行，任务就能用相对命令名。",
          "",
          "### 3. 修复 B：任务里用绝对路径 — **必做**",
          "",
          "```bash",
          "~/.nvm/versions/node/v20/bin/node /path/script.js",
          "```",
          "",
          "不依赖 PATH，最稳。",
          "",
          "### 4. 修复 C：任务内先加载环境 — **必做**",
          "",
          "```bash",
          ". \"$HOME/.bashrc\" && mycmd",
          "```",
          "",
          "注意 .bashrc 通常有非交互守卫；更稳的是把需要的东西写进独立 env 文件，任务里 `. /path/env.sh && mycmd`。",
          "",
          "### 5. 调试技巧 — **必做**",
          "",
          "任务输出重定向到日志（`>> /tmp/job.log 2>&1`）；失败时第一行里的 `xxx` 就是要补路径/变量的那个命令。"
        ],
        "怎么确认修好了": [
          "",
          "任务正常执行（实测输出形态）：",
          "",
          "```text",
          "crontab 声明 PATH 后 → 任务日志输出 NODE-OK <时间>（实测）",
          "绝对路径版 → 同样正常执行（实测）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 传统 SysV cron | 默认 PATH=/usr/bin:/bin，顶部声明 PATH |",
          "| systemd cron | PATH 含 /usr/local/bin（实测），nvm/conda 目录仍需显式声明 |",
          "| nvm/conda/pyenv | 初始化在 .bashrc，cron 不加载 |",
          "| alias | cron 里不生效 |",
          "| 调试 | printenv 进任务，或 /proc/<cron pid>/environ |",
          ""
        ],
        "症状": [
          "",
          "crontab 定时任务跑起来后报找不到命令 / 环境不对：",
          "",
          "```",
          "# 任务里调用 node（nvm 装的）、conda、或自定义脚本，日志只有一行：",
          "/bin/sh: 1: demo-node: not found",
          "```",
          "",
          "特征：交互终端里命令明明能用（`which node` 有结果），但 cron 任务执行时 `command not found`；",
          "或者任务能跑但读不到环境变量（NVM_DIR、CONDA_PREFIX、自定义 export）；",
          "cron 日志里只有一行 `/bin/sh: N: xxx: not found`，没有更多线索；",
          "常见于：nvm/conda/pyenv 装的工具、`~/.bashrc` 里 export 的变量、需要登录 shell 才有的环境。",
          ""
        ],
        "原因": [
          "",
          "cron 执行任务的 shell 与我们交互终端的环境完全不同：",
          "- cron 用 `/bin/sh` 执行命令，**不加载** `~/.bashrc`、`~/.profile`、`/etc/profile`——nvm/conda/pyenv 的初始化（它们都写在 `~/.bashrc` 里）全部缺失；",
          "- 任务的 PATH 由 cron 守护进程决定：传统 SysV cron 默认 `/usr/bin:/bin`，不含 `/usr/local/bin`，更不含 nvm/conda 的自定义目录；",
          "- 反常识实测（本机 Ubuntu 26.04）：systemd 以 `cron -f -P` 启动时，任务继承的是 systemd 的 PATH",
          "  （`/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin`）——`/usr/local/bin` 其实可用，",
          "  真正中招的是 `~/.nvm/versions/.../bin`、conda 环境目录这类\"只在 .bashrc 里加\"的路径和变量；",
          "- 别名（alias）也一样：cron 环境里 alias 不生效。",
          ""
        ],
        "解法": [
          "",
          "1. **先确认任务的环境**：临时把 `printenv > /tmp/cron-env.txt 2>&1` 放进任务，跑一次看 PATH 和变量——",
          "   比猜快得多；或直接看 cron 守护进程环境：`tr '\\0' '\\n' < /proc/$(pgrep -x cron)/environ | grep PATH`。",
          "2. **修复 A：crontab 顶部声明 PATH**（最常用）：",
          "   `PATH=~/.nvm/versions/node/v20/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`",
          "   放在 crontab 第一行，任务就能用相对命令名。",
          "3. **修复 B：任务里用绝对路径**：`~/.nvm/versions/node/v20/bin/node /path/script.js`——不依赖 PATH，最稳。",
          "4. **修复 C：任务内先加载环境**：`* * * * * . \"$HOME/.bashrc\" && mycmd`（注意 .bashrc 通常有非交互守卫，",
          "   更稳的是把需要的东西写进一个独立 env 文件，任务里 `. /path/env.sh && mycmd`）。",
          "5. **调试技巧**：cron 任务输出重定向到日志（`>> /tmp/job.log 2>&1`），失败时第一行 `/bin/sh: N: xxx: not found`",
          "   里的 `xxx` 就是要补路径/变量的那个命令；修好后重跑一次并确认日志出现正常输出。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，Ubuntu 26.04 LTS，systemd cron，任务输出重定向捕获）：",
          "  - 环境基线：`cron -f -P`（systemd cron.service）→ cron 进程 PATH 实测为",
          "    `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin`；",
          "  - 模拟 nvm/conda：命令 `demo-node` 只装在 `~/nvmbin/`（不在上述 PATH）；",
          "  - 失败版：crontab 不声明 PATH，任务 `* * * * * demo-node >> /tmp/cron-debug.log 2>&1` →",
          "    下一次刻度后日志为 `/bin/sh: 1: demo-node: not found`（与 [SO #21775749](https://stackoverflow.com/q/21775749) 同款现象）；",
          "  - 修复 A：crontab 顶部 `PATH=$HOME/nvmbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` →",
          "    任务正常执行，日志输出 `NODE-OK <时间>`；",
          "  - 修复 B：任务改为 `~/nvmbin/demo-node` 绝对路径 → 同样正常执行；",
          "  - 清理：测试 crontab 已删除、cron 已停止、临时文件已删除（宿主 root crontab 无残留）。",
          "- 素材：[SO #21775749](https://stackoverflow.com/q/21775749)《Command not found even crontab PATH is set》（老教程普遍只讲\"cron 默认 PATH=/usr/bin:/bin\"，",
          "  本条目补上了 systemd 场景的实测修正）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/crontab-env-path",
        "agent": "https://trove.xin/agent/problems/crontab-env-path"
      }
    },
    {
      "id": "css-load-failure",
      "title": "CSS 样式加载失败（页面无样式/布局错乱，控制台可能无报错）",
      "domain": "deploy",
      "domain_label": "构建与部署",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "nginx",
        "vite-deploy"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/shenchuanchao/p/22547449/css-load-failure-troubleshooting-complete-guide",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "页面打开后布局错乱、文字堆叠、样式\"消失\"，像纯文本页。最坑的是：Network 面板里 `.css` 状态码 **200**、Preview 内容完整，页面却依然无样式；控制台可能**完全没有报错**；普通模式异常、无痕模式反而正常。怎么查都查不到原因。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "浏览器按响应头 `Content-Type` 决定是否应用样式表——**状态码 200 不代表样式会被应用**。核心根因几乎都在服务器端的 MIME 配置：",
          "",
          "| 响应头 Content-Type | 浏览器行为 |",
          "|---|---|",
          "| `text/css`（正确） | ✅ 正常应用 |",
          "| `text/plain` | 当纯文本丢弃（nginx 缺 `include mime.types` 时默认兜底） |",
          "| `application/octet-stream` | 当二进制流丢弃 |",
          "| `text/css,text/html`（逗号拼接） | HTTP 标准只允许一个值，Edge 等严格浏览器静默丢弃（控制台无报错、状态码仍 200）——文章真实案例 |",
          "",
          "另外两个常见坑：**缓存陷阱**（修复后普通模式仍无样式、无痕模式正常——浏览器缓存了旧 CSS 及其响应头）；**字体图标方框**（CSS 正常但图标乱码——字体 MIME 不对）。",
          "",
          "浏览器是否应用样式表由响应头 `Content-Type` 决定，与状态码无关：200 但 `text/plain`/`application/octet-stream` 的 CSS 会被丢弃；HTTP 标准只允许一个 `Content-Type` 值，逗号拼接的多值（`text/css,text/html`）被严格浏览器静默忽略——控制台无报错、状态码 200，最难排查。",
          "",
          "缓存是第二层陷阱：修复后普通模式仍无样式、无痕模式正常 = 浏览器缓存了旧 CSS 及其响应头。",
          "",
          "确定性推论：无样式先看 Network 面板里 CSS 请求的响应头（Content-Type 是不是 text/css），再看是不是缓存（无痕模式对比）；服务器侧 nginx 缺 `include mime.types` 是最常见根因（默认兜底 text/plain）；修完加 `Cache-Control` 或改文件名破缓存；",
          "",
          "判断路径：200 + 控制台无错 = 一定在响应头或缓存，别动 CSS 内容。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：nginx 场景——补上 MIME 类型表并重启（.css → text/css）",
          "sed -i 's|# include mime.types;|include mime.types;|' /etc/nginx/nginx.conf && nginx -t && nginx -s reload",
          "```",
          "",
          "先 `nginx -t` 确认配置合法再 reload；Apache 用 `AddType text/css .css`，IIS 在 MIME 映射里把 `.css` 设为 `text/css`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 定位三步 — **排查**",
          "",
          "F12 控制台看有无报错 → Network 面板筛选 `.css` 看状态码 → **看响应头 `Content-Type`**（核心战场：200 + Preview 正常但页面无样式 = 一定是解读方式问题）。",
          "",
          "```bash",
          "curl -I https://你的域名/style.css",
          "```",
          "",
          "看 `Content-Type` 是不是 `text/css`。",
          "",
          "### 2. 修复服务器 MIME — **必做**",
          "",
          "nginx：确保有 `include mime.types;`，兜底设 `default_type application/octet-stream;`；或给 css 单独指定类型：",
          "",
          "```bash",
          "location ~ \\.css$ { types { text/css css; } }",
          "```",
          "",
          "Apache：`AddType text/css .css`；IIS：检查 MIME 映射（.css → text/css）、多余的自定义 Content-Type 响应头、web.config `<customHeaders>`。",
          "",
          "### 3. 字体图标方框：修字体 MIME — **必做**",
          "",
          "`.woff2` → `font/woff2`、`.woff` → `font/woff`、`.ttf` → `font/ttf`、`.otf` → `font/otf`、`.svg` → `image/svg+xml`、`.eot` → `application/vnd.ms-fontobject`。",
          "",
          "### 4. 缓存陷阱 — **必做**",
          "",
          "用户侧清缓存（Ctrl+Shift+Delete → 缓存的图像和文件）；开发者 Ctrl+F5 强刷；生产环境给 CSS 加版本号：",
          "",
          "```bash",
          "<link rel=\"stylesheet\" href=\"common.css?v=20260806\">",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "响应头断言（预期输出）：",
          "",
          "```text",
          "curl -I https://你的域名/style.css",
          "→ Content-Type: text/css",
          "```",
          "",
          "页面正常渲染样式；强刷后普通模式与无痕模式一致；字体图标不再显示方框。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| nginx | 缺 `include mime.types` 时按 `default_type` 兜底成 text/plain（实测复现）；补上即 text/css |",
          "| Apache | `AddType text/css .css` 等价修复 |",
          "| IIS | 三处叠加配置都要查：MIME 映射、自定义响应头、web.config |",
          "| 逗号拼接 Content-Type | 服务器不校验、原样发出，严格浏览器静默丢弃（实测复现） |",
          "| 缓存机制 | Last-Modified + If-Modified-Since → 304，浏览器缓存 CSS 及其响应头（实测复现） |",
          ""
        ],
        "症状": [
          "",
          "页面打开后布局错乱、文字堆叠、样式\"消失\"（像纯文本页）。特征：网络面板里 .css 状态码 200、Preview 内容完整，但页面依然无样式；控制台可能完全无报错（浏览器\"静默丢弃\"）；部分场景普通模式异常、无痕模式正常；字体图标显示为方框。",
          ""
        ],
        "原因": [
          "",
          "浏览器按响应头 `Content-Type` 决定是否应用样式表，服务器端 MIME 配置错误是核心根因（状态码 200 不代表样式会被应用）：",
          "",
          "1. **Content-Type 错误**：`text/plain`（当纯文本丢弃）、`application/octet-stream`（当二进制流）、`text/html`（拒绝应用）——常见于服务器未配置 MIME 类型表（如 nginx 缺 `include mime.types`，默认按 `default_type text/plain` 兜底）。",
          "2. **Content-Type 非法格式**：如 `text/css,text/html` 逗号拼接。HTTP 标准只允许一个值；服务器不校验、原样发出，Edge 等严格浏览器静默丢弃（控制台无报错、状态码仍 200）。这是文章真实案例（common.css）。",
          "3. **缓存陷阱**：修复服务器后普通模式仍无样式、无痕模式正常——浏览器缓存了旧 CSS 及其响应头，强刷/清缓存/加版本号解决。",
          "4. 其他：404/403/500、CORS 跨域拦截、浏览器扩展拦截（`ERR_BLOCKED_BY_CLIENT`）、SSL 证书过期（`ERR_CERT_DATE_INVALID`）。",
          ""
        ],
        "解法": [
          "",
          "1. **定位三步**：F12 控制台有无报错 → Network 面板筛选 `.css` 看状态码 → 看响应头 `Content-Type`（核心战场；200 + Preview 正常但页面无样式 = 100% 解读方式问题）。",
          "2. **修复服务器 MIME**：",
          "   - nginx：`include mime.types;`（`.css` → `text/css`），兜底 `default_type application/octet-stream;`；或指定 `location ~ \\.css$ { types { text/css css; } }`",
          "   - Apache：`AddType text/css .css`",
          "   - IIS：检查三处叠加配置——MIME 类型映射（.css 应为 text/css）、HTTP 响应标头自定义 Content-Type（有多余则删）、web.config `<customHeaders>` 节点",
          "3. **字体图标方框**（CSS 正常但图标乱码）：字体 MIME 也要对——`.woff2` → `font/woff2`、`.woff` → `font/woff`、`.ttf` → `font/ttf`、`.otf` → `font/otf`、`.svg` → `image/svg+xml`、`.eot` → `application/vnd.ms-fontobject`。",
          "4. **缓存陷阱**：用户侧清缓存（Ctrl+Shift+Delete → 缓存的图像和文件）/ 开发者 Ctrl+F5 强刷 / DevTools 禁用缓存；生产环境给 CSS 加版本号 `<link rel=\"stylesheet\" href=\"common.css?v=20260806\">`。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，nginx-light 1.28.3 + python3 http.server 3.14.4，纯本地零网络）：",
          "  1. **错误 MIME 复现**：python http.server 强制 `.css` → `Content-Type: text/plain`，curl -I 断言原样发出（浏览器将拒绝应用样式）。",
          "  2. **非法格式复现**：强制 `.css` → `Content-Type: text/css,text/html; charset=utf-8`，curl -I 断言服务器不校验、原样发出——复现文章真实案例（严格浏览器静默丢弃的服务器侧根因）。",
          "  3. **nginx 缺 mime.types**：`/style.css` → `Content-Type: text/plain`（默认 default_type 兜底）；加 `include mime.types` → `text/css`——文章 §5.2 修复命令成立。",
          "  4. **缓存机制**：`Last-Modified` + `If-Modified-Since` → `304 Not Modified`，证明浏览器会缓存 CSS 及其响应头；修复后需强刷或加版本号。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/css-load-failure",
        "agent": "https://trove.xin/agent/problems/css-load-failure"
      }
    },
    {
      "id": "curl-resume-break",
      "title": "大文件下载中断后无法续传（curl: (33) HTTP server does not seem to support byte ranges）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "curl"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/keenerd/packer/issues/136",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "下载大文件（模型、镜像、数据集）中断后想续传：`curl -C - -o model.bin URL` 报 `curl: (33) HTTP server does not seem to support byte ranges. Cannot resume.`，curl 退出、文件保持部分状态；换 wget `-c` 同样失败。**不是你的命令写错，是服务器不支持断点续传。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "HTTP 断点续传依赖 `Range` 头：客户端发 `Range: bytes=N-`，服务器返回 `206 Partial Content` + `Content-Range`。服务器不支持（返回 200 全量或 416）时，curl 无法拼接，只能报 33 放弃。",
          "",
          "常见于：小型自建服务器/内网下载站、`python3 -m http.server`（3.14 实测不支持 Range）、某些网盘直链。",
          "",
          "| 服务器 | Range 支持 |",
          "|---|---|",
          "| nginx / CDN / 对象存储 | ✅ 支持（默认） |",
          "| python3 -m http.server（3.14） | ❌ 实测不支持 |",
          "| 小型自建/网盘直链 | 看实现 |",
          "",
          "断点续传是「客户端 Range + 服务器 206」的协议协商：curl 发 `Range: bytes=N-`，服务器支持就返回 `206 Partial Content` + `Content-Range` 告诉它从哪续；不支持就返回 200（全量）或 416（范围不合法）——curl 无法在「从头开始的 200 全量」上拼接，只能报错 33 放弃。",
          "",
          "所以续传失败 = 服务器没实现 Range，不是 curl 或网络问题。",
          "",
          "python3 -m http.server 在 3.14 实测不支持 Range；nginx/CDN/对象存储默认支持。",
          "",
          "确定性推论：`curl -C -` 报 33 先探服务器：`curl -I -H \"Range: bytes=0-0\" <url>` 看返回 206 还是 200/416；206 = 支持续传，200/416 = 服务器不支持，换下载通道（nginx/CDN/对象存储）或用支持断点的工具（wget -c 对部分服务器同协议，同样依赖 206）；判断路径固定：失败点不在客户端。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先探测服务器是否支持 Range（206=支持，200=不支持）",
          "curl -sI -H \"Range: bytes=0-99\" URL",
          "```",
          "",
          "支持就 `curl -C - -o 文件 URL` 续传；不支持就换源或整包重下。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先探测服务器是否支持 Range — **排查**",
          "",
          "```bash",
          "curl -sI -H \"Range: bytes=0-99\" URL",
          "```",
          "",
          "返回 `206` 支持，返回 `200` 不支持。",
          "",
          "### 2. 支持就续传 — **必做**",
          "",
          "```bash",
          "curl -C - -o 文件 URL",
          "```",
          "",
          "自动从已有文件大小继续；wget 用 `wget -c URL`。",
          "",
          "### 3. 不支持 Range 的源 — **必做**",
          "",
          "换支持 Range 的下载源（CDN/对象存储/官方镜像站）；或 `aria2c -c URL`（多线程分段下载，对源要求更高）；或直接整包重下（删掉部分文件，避免误用不完整文件）。",
          "",
          "### 4. 脚本里处理 33 — **必做**",
          "",
          "```bash",
          "curl -C - -o file URL || echo \"续传失败，检查服务器 Range 支持\"",
          "```",
          "",
          "下载工具/脚本里先检查 `-C -` 返回码，33 时告警而不是静默当成功；续传完成后务必校验哈希（`md5sum -c` / sha256），部分文件拼接错位很隐蔽。"
        ],
        "怎么确认修好了": [
          "",
          "续传成功且哈希一致（实测输出形态）：",
          "",
          "```text",
          "curl -C - -o part.bin URL",
          "→ exit 0，md5 与源文件一致（实测 4211ba8d）",
          "```",
          "",
          "失败链路：不支持 Range 的服务器 → `curl: (33)` exit 33（实测，与 packer#136 同款）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 支持 Range | `curl -C -` 直接续传（实测 206 + Content-Range 链路） |",
          "| 不支持 Range | 换源 / aria2c / 整包重下 |",
          "| python3 -m http.server | 3.14 实测不支持 Range，本地调试别用 |",
          "| 探测法 | `curl -sI -H \"Range: bytes=0-99\"`，206 vs 200 |",
          "| 本地测试 | 用 nginx（默认支持）或自写 206 服务器 |",
          ""
        ],
        "症状": [
          "",
          "下载大文件（模型、镜像、数据集）中断后想续传：",
          "",
          "```",
          "$ curl -C - -o model.bin https://example.com/model.bin",
          "** Resuming transfer from byte position 10485760",
          "curl: (33) HTTP server does not seem to support byte ranges. Cannot resume.",
          "```",
          "",
          "特征：`curl -C -` 从已下载位置续传，但服务器不支持 `Range` 请求（或忽略 Range 返回 200 全量）；",
          "报错 33 后 curl 退出，文件保持部分状态；换 wget `-c` 同样失败。",
          ""
        ],
        "原因": [
          "",
          "HTTP 断点续传依赖 `Range` 头：客户端发 `Range: bytes=N-`，服务器返回 `206 Partial Content` + `Content-Range`。",
          "若服务器不支持（返回 200 全量或 416），curl 无法拼接，只能报 33 放弃。",
          "常见于：小型自建服务器/内网下载站、python3 -m http.server（3.14 实测不支持 Range）、某些网盘直链。",
          "注意：python 的 `http.server` 在新版本里已不支持 Range——本地调试断点续传时别用它。",
          ""
        ],
        "解法": [
          "",
          "1. **先探测服务器是否支持 Range**：`curl -sI -H \"Range: bytes=0-99\" URL` ——返回 `206` 支持，返回 `200` 不支持。",
          "2. **支持就续传**：`curl -C - -o 文件 URL`（自动从已有文件大小继续）；wget 用 `wget -c URL`。",
          "3. **不支持 Range 的源**：换支持 Range 的下载源（CDN/对象存储/官方镜像站）；",
          "   或换 aria2c（`aria2c -c URL`，多线程分段下载，对源要求更高）；",
          "   或直接整包重下（部分文件删掉，避免误用不完整文件）。",
          "4. **下载工具/脚本里**：先检查 `-C -` 返回码，33 时告警而不是静默当成功；",
          "   续传完成后务必校验哈希（`md5sum -c` / sha256），部分文件拼接错位很隐蔽。",
          "5. **本地测试断点续传**：用 nginx（默认支持 Range）或自写 206 服务器；别用 python3 -m http.server。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，curl 8.x，50MB 随机文件，纯本地）：",
          "  - 成功链路：自写支持 Range 的服务器（206 + Content-Range）→ `timeout 0.3 curl -o part.bin` 中断得 15.5MB 部分文件 →",
          "    `curl -C - -o part.bin` 续传 exit 0，md5 与源文件一致（4211ba8d）；",
          "  - 失败链路：不支持 Range 的服务器（忽略 Range 恒返回 200 全量）→ 部分文件 10MB 续传 →",
          "    `curl: (33) HTTP server does not seem to support byte ranges. Cannot resume.`（exit 33），与 packer#136 同款；",
          "  - 冷知识对照：`python3 -m http.server`（3.14.4）对 `-C -` 同样报 33——新版本 http.server 不支持 Range，本地调试别用；",
          "  - 探测法验证：`curl -sI -H \"Range: bytes=0-99\"` 对自写服务器返回 206，对 norange/http.server 返回 200。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/curl-resume-break",
        "agent": "https://trove.xin/agent/problems/curl-resume-break"
      }
    },
    {
      "id": "debugging-methodology-consensus",
      "title": "诡异 Bug 查不出来？不是运气差——把排障从「猜」变成「证据链」",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（5 篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个我至今记得的下午。同事跑过来：「Google 能打开，但那个视频站打不开了，是不是它挂了？」我第一反应也是「那网站挂了呗」。但多问了一句：同一台电脑、同一个浏览器、同一时间，一个域名通一个不通——如果是网站挂了，为什么不是全站都挂？这个「为什么」就是排障的起点：**绝大多数「诡异 bug」，不是问题真的诡异，是我们把「表象」当成了「事实」**。",
          "",
          "类似的场景你一定见过：服务重启一下就好了，但过几天又挂，你怀疑是「偶发」；接口偶尔超时，你怀疑是「网络波动」；只有一台机器限流，你怀疑是「流量变大了」。然后你开始猜：改配置、重启、加超时、调参数……运气好蒙对了，运气不好折腾一晚上，第二天它自己又好了——于是你更坚信它是「玄学」。",
          "",
          "我见过最典型的反面教材：一个考务系统，报名高峰时网站打不开，运维重启就恢复，跑几天又打不开。反复重启了快一个月，直到有人发现故障时间点都发生在**有人上传 ZIP 附件的时候**——提取样本一看，是恶意构造的 ZIP 炸弹：文件在内存里解压，直接吃爆 Java 堆。如果早一个月把「故障时间」和「用户操作」关联起来，这个事故根本不用扛一个月。",
          "",
          "还有一次异地多活切换，限流告警响了。第一反应都是「流量变大了」——但把异常时段和正常时段的请求曲线一对比，19:16 的请求量只有 60 多，还不如平时高峰的零头；再看，限流只出现在**一台机器**上。流量没变大、其他机器都正常——限流是果，不是因。往单机里挖，才发现是 GC 停顿把请求处理卡死了。如果当时顺着「流量变大」的直觉去扩容，扩了也白扩。",
          "",
          "如果你也经历过「重启大法」「改配置玄学」「怀疑人生一整晚」——这篇文章是写给同类人的。它不是某个 bug 的教程，是把多次独立事故里反复验证过的排障方法，提炼成一条可复制的**证据链**：先分层、再取证、找变量、对基线、拿样本。用这套方法，诡异 bug 的「诡异」会消失大半。",
          ""
        ],
        "原因": [
          "",
          "先想清楚：为什么我们遇到 bug 第一反应是「猜」，而不是「查」？",
          "",
          "**第一，人脑的归因机制是「就近联想」，不是「系统分析」。** 网站打不开，最近的联想是「网站挂了」；接口超时，最近的联想是「网络不好」；重启好了，最近的联想是「是偶发」。这不是笨，是大脑默认走捷径。",
          "",
          "而系统故障恰恰是分层的：一个请求从 DNS 解析到 TCP 连接、TLS 握手、HTTP 请求、应用处理、数据库查询，任何一层出错，表象都是「打不开」「超时」「报错」——**表象相同，层不同，修法完全不同**。猜，就是在七层里随机挑一层赌。",
          "",
          "**第二，我们把「故障」和「根因」混为一谈。** 限流告警是故障，但限流往往是果不是因——真正的原因可能在 GC、线程池、连接池、慢查询。看到告警就处理告警，等于看到发烧就只吃退烧药，炎症还在。排障的难点从来不是「修」，是「把果剥到因」。",
          "",
          "**第三，我们急着「修好」，不愿意「取证」。** 重启是取证的天敌：重启把现场销毁了。很多诡异 bug 之所以诡异，是因为每次都被重启/重试「治好」了，永远没机会留下证据。排障和破案一样，现场没了，案子就没了。",
          "",
          "下面每条经验，都是把排障从「猜」拉回「证据链」的做法——它们不是一家之言，是几套独立事故处理反复验证过的共识。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：排障五步——分层、取证、找变量、对基线、拿样本",
          "",
          "**遇到诡异 bug，按顺序走：①把表象拆成链路，逐层排除（DNS/TCP/TLS/HTTP/应用/数据库）；②每个结论都要有可复现的测试支撑，用词严谨到可以被推翻；③找「变量」和「触发点」——故障一定是某个变化引发的，重启恢复又复发 = 触发式故障；④拿异常时段和正常基线对比，把「想当然」排除掉；⑤先复现、拿样本，再动手修。** 赶时间只看这一条。下面经验 1~5 是为什么和怎么做。",
          "",
          "### 经验 1：先分层，把「表象」拆成「链路」",
          "",
          "现象：一个网站打不开，多数人的第一反应是「网站挂了」。但「某个域名打不开」只是表象——故障可能发生在 DNS 解析、TCP 连接、TLS 握手、HTTP 请求、CDN、代理、甚至浏览器扩展的任何一层。",
          "",
          "我在一次真实排查里见过完整的链条：Google 能打开、某个视频站打不开，逐层测下来——DNS 解析用的是 `nslookup` 发现解析超时，用 `curl --resolve` 绕过 DNS 直连 CDN 后 HTTP 200——故障被精确定位到「当前网络的 DNS 解析链路」，而不是网站本身。",
          "",
          "做法：①先画链路：客户端 → DNS → TCP → TLS → HTTP → 应用 → 数据库，每一层都有对应的验证命令；",
          "",
          "②用工具逐层打点：`nslookup`/`dig` 查解析，`curl -v` 看卡在哪个阶段（`Connected to` 之前是 DNS/TCP，`SSL connected` 之后才是 TLS/HTTP），`curl --resolve` 绕过某层验证下一层，`ping`/`traceroute` 看路由；",
          "",
          "③「某层正常」和「某层出错」都要记录，排除法做完，剩下的那层就是嫌疑层。",
          "",
          "为什么：系统是分层的，表象不是事实——「打不开」只是最外层的信息，真正的故障层级决定了完全不同的修法（改 DNS 配置 vs 修证书 vs 调应用）。逐层打点不是慢，是把「猜七层」变成「查一层」。",
          "",
          "### 经验 2：证据链思维——结论可以窄，但必须实",
          "",
          "现象：DNS 排查到最后，结论是「当前网络的 DNS 解析链路异常」。当时有人问：「那就是运营商屏蔽了呗？」——不。客户端测试只能证明故障发生在 DNS 解析这一层，至于路由器内部是缓存异常、上游 DNS 故障、还是过滤规则，客户端证据无法唯一确定。**把结论说窄，不是保守，是诚实**：窄结论可以被下一步证据推翻或坐实，宽结论（「运营商屏蔽」）一旦说出口，排查就停了。",
          "",
          "做法：①每个结论都要附带「我怎么测的、复现命令是什么」；②用词分级：「异常」≠「屏蔽」，「疑似」≠「实锤」，「当前网络」≠「全局」；③结论写出来先自我检查：这句话能被什么证据推翻？如果什么都推翻不了，它就不是结论，是猜测。",
          "",
          "为什么：排障最大的风险不是「不知道」，是「知道错了」——一个听起来合理的错误结论会终止排查（「运营商屏蔽，没办法」），而一个严谨的窄结论会引导下一步取证。证据链的终点是「可复现」，不是「听起来对」。",
          "",
          "### 经验 3：找「变量」和「触发点」，别信「偶发」",
          "",
          "现象：考务系统反复打不开、重启就恢复——「偶发」这个词害死人。直到有人把故障时间点和操作关联起来：**每次故障都发生在有人上传 ZIP 附件之后**。顺着这个变量提取样本，发现是恶意 ZIP（ZIP 炸弹：压缩包极小，解压后是巨量重复数据，在内存里直接解爆 Java 堆）。一个月的问题，半小时定位。",
          "",
          "做法：①故障发生时立刻记三件事：时间、最近一次变更（发布/配置/操作）、故障范围（单机/全局限定范围）；②「重启恢复」不是结论，是线索——重启恢复又复发 = 触发式故障，一定有个触发条件没被找到；③把「故障时间线」和「操作时间线」并排画，找重叠点；④怀疑「偶发」前，先问自己：有没有连续记录过三次以上的故障上下文？没有记录，就没有资格说「偶发」。",
          "",
          "为什么：计算机系统里没有「偶发」——同一输入同一状态必然同一输出。所谓偶发，只是触发条件还没找到。而触发条件几乎总是「变量」：一次发布、一个操作、一个特殊输入。找变量比找根因快，因为变量是可见的、可比的。",
          "",
          "### 经验 4：对比基线，限定范围——把「想当然」排除掉",
          "",
          "现象：异地多活切换后限流告警，第一反应是「流量变大了」。把异常时段的请求曲线和正常基线一对比：19:16 请求量 60 多，平时高峰 400 多——流量根本没变大；再看，限流只出现在一台机器上。两个对比做完，「流量变大」被排除，问题锁定单机，往里挖才发现是 GC 停顿。如果跟着直觉去扩容，扩了也白扩。",
          "",
          "做法：①任何「因为 A 所以 B」的归因，先找对照：异常时段 vs 正常时段、故障机 vs 健康机、改前 vs 改后；②数据说话：把曲线、日志、指标拉出来对比，别凭感觉；③限定范围：单机问题 vs 全局问题、单用户 vs 全用户、单接口 vs 全接口——范围越小，变量越少；④「排除法」的每个排除都要有证据，排除完剩下的就是答案，不用猜。",
          "",
          "为什么：归因的常见死法是「想当然」——流量大、网络差、机器烂，都是听起来合理的解释。对比基线是成本最低的证伪手段：一个曲线对比就能排除「流量变大」，一个单机对比就能排除「全局故障」。限定范围是把「大海捞针」变成「碗里捞针」。",
          "",
          "### 经验 5：先复现、拿样本、再动手修",
          "",
          "现象：考务系统事故里，如果只是「关闭上传接口」就完事，ZIP 炸弹的根因（内存解压 + 无大小限制）永远不会被发现，换个入口（比如表单导入）还会炸。真正的处理是：提取样本 → 分析构造（重复字符 + 极小压缩比 = 炸弹特征）→ 修代码（解压到磁盘 + 限制大小）→ 验证。",
          "",
          "再比如一次 yfinance 调试：Chart API 正常、历史数据超时，看起来是「网络问题」——实际进源码看，是 SOCKS5 代理下某个请求路径没有走代理逻辑。",
          "",
          "不拿样本、不看源码，永远到不了这一步。",
          "",
          "做法：①修复前先问：这个 bug 我能复现吗？不能复现的修复是盲修；②尽量留下现场：日志、堆栈、请求样本、复现脚本——重启前先 dump；③拿到的样本要「解剖」：不只看报错表面，看数据的构造（大小、内容、结构），样本会说真话；④修完必须验证「同类输入不再触发」，不只是「这个输入不报错」。",
          "",
          "为什么：复现是根因分析的唯一可靠前提——不能复现，就无法验证修复是否真的有效，只能「赌」。样本是现场的唯一证据，解剖样本才能从「修表象」到「修根因」。排障的最后一步不是「修好了」，是「我知道了为什么，并且同类不会再犯」。",
          ""
        ],
        "怎么迁移到你的场景": [
          "",
          "这套方法不挑技术栈，挑的是**习惯**。落地三步：",
          "",
          "1. **建一个「排障模板」**（笔记软件里存一份）：分层链路清单（你的系统是哪些层）、常用取证命令、时间/变更/范围三要素记录表。下次故障直接照着填，不用现场想。",
          "2. **把「重启大法」升级成「重启前取证」**：重启前至少留三样东西——报错原文、最近变更、故障时间。哪怕这次没时间查，样本留着，下次复现时它就是破案关键。",
          "3. **每周花 10 分钟复盘一次「本周 bug」**：不用写报告，就问三个问题——我当时第一反应是什么？我有没有先取证再动手？如果重来一次，哪一步能更快？三个月后你会明显感觉到，「诡异」变少了。",
          "",
          "最后一句话：排障能力不是天赋，是**取证纪律**。每次忍住「先猜一把」的冲动、多跑一条验证命令，都是在给下一次事故攒证据。",
          "",
          "收尾：写这篇之前，我刚又犯过一次「想当然」——线上问题查了两个小时，最后发现是缓存没失效，而我的第一反应是「代码肯定没问题」。排障最贵的从来不是工具，是「我觉得」这三个字。把五个步骤贴在工位上：分层、取证、找变量、对基线、拿样本。下次再遇到诡异 bug，先走完流程再下结论——你会少熬很多个夜。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/debugging-methodology-consensus",
        "agent": "https://trove.xin/agent/problems/debugging-methodology-consensus"
      }
    },
    {
      "id": "deepcode-2",
      "title": "git clone 连接被重置（源码安装路径）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "deepcode"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`git clone https://github.com/HKUDS/DeepCode.git` 反复报连接被重置 / 超时。你以为换个网络就行，结果换完还是断。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "两个常见原因：",
          "",
          "| 原因 | 说明 |",
          "|---|---|",
          "| 国内直连 GitHub 不稳定 | 大仓库 clone 中途被重置（连通性记录） |",
          "| 本地配置了失效的 Git 代理 | 代理已挂但 `http.proxy` 还留着，git 走死代理必失败 |",
          "",
          "还有一个关键认知：**你根本不需要 clone**——只想用工具直接 pip 安装即可。",
          "",
          "git 的传输路径是“**代理配置 → TCP 连接 → TLS 握手 → 数据流**”四段，报 `Connection reset by peer` 时前三段已经走完、在数据流阶段被对端掐断——说明网络是通的，断的是“传输通道稳定性”，不是“连不上”。",
          "",
          "两个常见根因对应两段：**本地失效代理**（git 读取 `http.proxy`/`https.proxy` 全局配置，把流量交给一个已经挂掉的代理，代理直接拒绝/重置）和**直连出口不稳**（大仓库长连接被中间设备重置）。",
          "",
          "这解释了为什么“换个网络还是断”：如果根因是失效代理，换网络没用，代理配置还在；所以排查顺序固定：先 `git config --get http.proxy` 确认有没有残留代理并清掉（改配置层），再换网络试（改物理层），最后才考虑源头。还有个更省事的确定性选择：**装工具 ≠ 装源码**——只想用工具就 `pip install`，把整个 clone 环节跳过，问题直接不存在。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：清掉失效 git 代理，然后直接 pip 安装（不需要 clone 源码）",
          "git config --global --unset http.proxy && git config --global --unset https.proxy && pip install deepcode-hku",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 清代理 — **必做**",
          "",
          "```bash",
          "git config --global --unset http.proxy",
          "```",
          "",
          "和 `git config --global --unset https.proxy`，再检查系统代理环境变量（`env | grep -i proxy`）。",
          "",
          "### 2. 换网络/热点重试 — **必做**",
          "",
          "换网络或错峰重试；热点网络常常能绕开出口问题。",
          "",
          "### 3. 只想用工具：直接 pip 安装 — **必做**",
          "",
          "```bash",
          "pip install deepcode-hku",
          "```",
          "",
          "不需要 clone 源码（本市场收录走 PyPI 路径）。"
        ],
        "怎么确认修好了": [
          "",
          "clone 或 pip 安装正常完成：",
          "",
          "```text",
          "git clone ... → 正常完成（或）",
          "pip install deepcode-hku → Successfully installed deepcode-hku",
          "```",
          "",
          "不再出现 `Connection reset by peer`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 失效代理 | `--unset` 后恢复（CSDN 案例） |",
          "| 直连不稳 | 换网络/热点/错峰 |",
          "| 只要工具 | pip 路径，跳过 clone |",
          "| 国内连通性 | GitHub 直连风险见  |",
          ""
        ],
        "症状": [
          "",
          "",
          "`git clone https://github.com/HKUDS/DeepCode.git` 反复报连接被重置 / 超时。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "国内直连 GitHub 不稳定；本地配置了失效的 Git 代理也会导致重置。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 清代理：`git config --global --unset http.proxy`（与 `https.proxy`），或检查系统代理环境变量。",
          "2. 换网络/热点重试；错峰重试。",
          "3. 只想用工具：直接用 pip 安装（路径 1），不需要 clone 源码。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（git clone 连接被重置 → 清 Git 代理/换热点，2026-06）。",
          "- 本市场实测：收录走 PyPI 路径（不需要 clone）；国内 GitHub 直连风险在连通性记录中标注。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/deepcode-2",
        "agent": "https://trove.xin/agent/problems/deepcode-2"
      }
    },
    {
      "id": "deepcode-3",
      "title": "curl 证书报错（--ssl-no-revoke）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "deepcode"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Windows 下用 curl 下载/安装相关脚本时，报证书错误（`SSL certificate problem` / revocation 相关）。脚本没跑起来，先倒在证书上。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Windows 证书吊销检查（revocation）在部分网络/代理环境下会失败；自签名或代理证书不被 curl 信任，导致 TLS 校验失败。 TLS 校验其实是**两层**：证书链校验（证书是否由受信 CA 签发）+ 吊销检查（证书是否已被吊销）。",
          "",
          "Windows 的 curl 默认开启吊销检查，而 Linux 默认关闭——这是两个平台行为差异的根源。吊销检查要联网访问 CRL/OCSP 服务，在代理/受限网络里常常够不到，Windows 就把这次连接判死。",
          "",
          "`--ssl-no-revoke` 只跳过吊销这一层，**不影响链校验**：证书链本身不合法（比如自签名）时它救不了，那种场景该做的是把证书加进信任库，而不是用 `-k` 关掉所有校验。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：Windows curl 安装脚本场景加 --ssl-no-revoke（跳过吊销检查）",
          "curl --ssl-no-revoke -sL -o deepcode-install.ps1 https://example.com/install.ps1",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 安装脚本场景加 --ssl-no-revoke — **必做**",
          "",
          "Windows curl 参数，跳过证书吊销检查（CSDN 2026-01 案例路径）。",
          "",
          "### 2. 或改用 pip 路径安装 — **必做**",
          "",
          "```bash",
          "pip install deepcode-hku",
          "```",
          "",
          "pip 的 truststore 处理证书更稳。",
          "",
          "### 3. 虚拟环境内操作 — **必做**",
          "",
          "```bash",
          "python -m venv .venv && .venv/bin/pip install deepcode-hku",
          "```",
          "",
          "避免系统级改动。"
        ],
        "怎么确认修好了": [
          "",
          "下载/安装完成（预期形态）：",
          "",
          "```text",
          "curl --ssl-no-revoke ... → 文件完整下载",
          "pip install deepcode-hku → Successfully installed",
          "```",
          "",
          "不再报 `SSL certificate problem`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows curl | `--ssl-no-revoke`（CSDN 案例）；本市场未实测，如实标注 |",
          "| Linux/WSL | 本市场实测无此问题 |",
          "| pip 路径 | truststore 处理证书更稳 |",
          "| 代理/自签名证书 | 吊销检查失败场景的根因 |",
          ""
        ],
        "症状": [
          "",
          "",
          "Windows 下用 curl 下载/安装相关脚本时报证书错误（`SSL certificate problem` / revocation 相关）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "Windows 证书吊销检查（revocation）在部分网络/代理环境下失败；自签名或代理证书不被 curl 信任。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 安装脚本场景加 `--ssl-no-revoke`（Windows curl 参数）。",
          "2. 或改用 pip 路径安装（pip 的 truststore 处理证书更稳）。",
          "3. 建议在虚拟环境内操作，避免系统级改动。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（curl 证书报错 → --ssl-no-revoke + 虚拟环境，2026-01）。",
          "- 本市场实测：Linux/WSL 环境无此问题；Windows curl 场景如实标注未实测。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/deepcode-3",
        "agent": "https://trove.xin/agent/problems/deepcode-3"
      }
    },
    {
      "id": "deepcode-4",
      "title": "旧 Python 装不上（requires-python）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "deepcode"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Python 3.8 等旧版本执行 `pip install deepcode-hku`，报找不到匹配版本。包是好的，是你的 Python 太旧。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "包要求 **Python >= 3.10**（新版依赖如 pydantic 2.x 不支持 3.8）；pip 按 `requires-python` 过滤后，你的解释器下**无可用版本**。",
          "",
          "| Python 版本 | 结果 |",
          "|---|---|",
          "| 3.8 及更旧 | ❌ 无匹配版本（CSDN 2025-08 案例） |",
          "| 3.10+ | ✅ 正常（本市场实测 3.14.4） || 3.10+ | ✅ 正常（本市场实测 3.14.4） |",
          "",
          "pip 的报错容易误导人：\"No matching distribution\" 看起来像\"包不存在\"，其实是**版本过滤器把候选全滤掉了**。pip 解析时会读取每个发行版的 `requires-python` 元数据，和当前解释器版本比对，不满足的直接不进入候选集；所有候选都被滤掉后，就报\"找不到匹配版本\"。包在 PyPI 上好好的，只是你的解释器不在支持列表里。",
          "",
          "所以遇到这个报错，第一步永远是 `python3 --version` 确认自己的版本，而不是怀疑包名拼错了。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认版本，3.10+ 直接装",
          "python3 --version && pip install deepcode-hku",
          "```",
          "",
          "版本 <3.10 就先升级 Python（多版本共存用 pyenv/conda，别动系统默认）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 Python 版本 — **必做**",
          "",
          "```bash",
          "python3 --version",
          "```",
          "",
          "### 2. 升级到 3.10+ — **必做**",
          "",
          "多版本共存用 pyenv/conda 管理，不要动系统默认 Python（避免破坏系统工具依赖）。",
          "",
          "### 3. 重新安装 — **必做**",
          "",
          "```bash",
          "pip install deepcode-hku",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "安装成功（实测输出形态）：",
          "",
          "```text",
          "pip install deepcode-hku",
          "→ Successfully installed deepcode-hku（Python 3.14.4 实测）",
          "```",
          "",
          "`deepcode --version`（或等价命令）正常输出版本。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Python 3.8 | 无匹配版本，升级到 3.10+ |",
          "| Python 3.14 | 实测安装成功 |",
          "| 多版本共存 | pyenv/conda，不动系统默认 |",
          "| Windows | 同版本要求，python.org 安装包升级 |",
          ""
        ],
        "症状": [
          "",
          "",
          "Python 3.8 等旧版本 `pip install deepcode-hku` 报找不到匹配版本。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "包要求 Python >= 3.10（新版依赖如 pydantic 2.x 不支持 3.8）；pip 按 requires-python 过滤后无可用版本。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 升级 Python 到 3.10+（本市场实测 3.14.4 正常）。",
          "2. 多版本共存用 pyenv/conda 管理，不要动系统默认。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（Python>=3.10，3.8 下载不了 deepcode 包，2025-08）。",
          "- 本市场实测：Python 3.14.4 安装成功（+ 闭环日志）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/deepcode-4",
        "agent": "https://trove.xin/agent/problems/deepcode-4"
      }
    },
    {
      "id": "deepcode-5",
      "title": "MCP client 起不来（Brave MCP WinError 2 / npx 未跑起来）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "deepcode"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "配置 MCP 客户端（如 Brave Search MCP）后 `deepcode mcp` 报 WinError 2 / 找不到命令；npx 没跑起来。客户端配置没问题，是**启动环境**有问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "MCP 客户端靠 **npx/Node 环境**启动：",
          "",
          "| 原因 | 表现 |",
          "|---|---|",
          "| Node 不在 PATH | 找不到命令 / WinError 2 |",
          "| npx 首次下载超时 | 首次跑包要下载，网络不稳就启动失败 || npx 首次下载超时 | 首次跑包要下载，网络不稳就启动失败 |",
          "",
          "WinError 2 是 Windows 的 \"系统找不到指定的文件\"——它不是 MCP 配置错，而是 **deepcode 拉起子进程时找不到 npx.exe**。GUI/服务环境里的 PATH 往往和终端里不一样（有些服务环境甚至不继承用户 PATH），所以终端里 `npx` 好好的，客户端一启动就 WinError 2。",
          "",
          "另一个隐蔽点：npx 首次运行要现场下载包，这步是网络操作，超时也算\"启动失败\"——所以先手动 `npx -y <包名>` 预下载一次，把\"网络问题\"和\"配置问题\"两个变量拆开。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认 npx 可用，并手动预下载 MCP 包",
          "npx --version && npx -y <mcp包名>",
          "```",
          "",
          "预下载成功后，再配置到 deepcode。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 Node/npx 可用 — **必做**",
          "",
          "```bash",
          "npx --version",
          "```",
          "",
          "报找不到命令就先装 Node 并确认 PATH。",
          "",
          "### 2. 手动先跑一次 npx 预下载 — **必做**",
          "",
          "```bash",
          "npx -y <mcp包名>",
          "```",
          "",
          "先把包拉下来，再配置到 deepcode，避免配置后首次启动超时。",
          "",
          "### 3. Windows 注意 PATH 与 cmd 权限 — **必做**",
          "",
          "服务类 MCP 用完整路径配置；Windows 下注意 cmd 执行权限。"
        ],
        "怎么确认修好了": [
          "",
          "MCP 客户端正常启动（预期形态）：",
          "",
          "```text",
          "deepcode mcp",
          "→ MCP 服务正常启动/握手（不再报 WinError 2）",
          "```",
          "",
          "`npx --version` 正常输出版本。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Node 不在 PATH | 装 Node 并加 PATH（CSDN 案例） |",
          "| 首次下载超时 | 手动 npx 预下载后再配置 |",
          "| Windows | 完整路径配置 + cmd 权限 |",
          "| 未实测项 | MCP client 实跑需 Node 生态与外部服务，本市场未实测 |",
          ""
        ],
        "症状": [
          "",
          "",
          "配置 MCP 客户端（如 Brave Search MCP）后 `deepcode mcp` 报 WinError 2 / 找不到命令；npx 没跑起来。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "MCP 客户端靠 npx/Node 环境启动；Node 不在 PATH、或 npx 首次下载超时都会导致启动失败。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 确认 Node/npx 可用：`npx --version`。",
          "2. 手动先跑一次 npx 预下载（`npx -y <mcp包>`），再配置到 deepcode。",
          "3. Windows 下注意 PATH 与 cmd 权限；服务类 MCP 用完整路径配置。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（Brave MCP WinError 2 / npx 未跑起来，2026-01）。",
          "- 本市场实测：deepcode mcp 命令面存在；MCP client 实跑需 Node 生态与外部服务，未实测。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/deepcode-5",
        "agent": "https://trove.xin/agent/problems/deepcode-5"
      }
    },
    {
      "id": "deepcode-6",
      "title": "装完跑 agent 无响应（需 init + provider 配置）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "deepcode"
      ],
      "evidence": "site docs#6;local repro log;local repro log;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`deepcode` 启动后交互无响应，或 agent 任务报错；`deepcode loop/goal` 不工作。装好了却跑不动——因为你**还没初始化、没配 LLM**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "首次使用需要两步配置：",
          "",
          "| 步骤 | 作用 |",
          "|---|---|",
          "| `deepcode init` | 生成 `~/.deepcode` 配置目录 |",
          "| `deepcode provider` | 配置 LLM 连接与模型 key |",
          "",
          "**没配 LLM 时 agent 链路空转**，交互自然无响应。",
          "",
          "首次使用的“无响应”是**链路断在配置层**：DeepCode 的 agent 链路是“用户输入 → agent 规划 → 调用 LLM → 输出”，LLM 是链路里必需的执行者。",
          "",
          "`deepcode init` 只生成配置目录（骨架），`deepcode provider` 才写入“LLM 连接与 key”（动力源）——两步配置分别对应“骨架”和“动力”，缺了第二步，链路每一步都走到“调用 LLM”就空转，交互自然无响应。",
          "",
          "这也定义了验证顺序：先确认骨架在（`~/.deepcode` 目录存在）→ 再确认动力在（provider 配置含有效的 key）→ 最后发一条真实请求验证链路通。还有一个容易踩的点：key 是外部凭据，配置时写错、过期、或账号无额度，表现同样是“无响应/报错”——所以验证链路时先确认 key 本身有效（用 provider 自己的测试命令或最小请求），再怀疑工具。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：init + provider 两步配置后重试",
          "deepcode init && deepcode provider",
          "```",
          "",
          "按提示配置模型与 key，然后重跑任务。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 初始化 — **必做**",
          "",
          "```bash",
          "deepcode init",
          "```",
          "",
          "生成 `~/.deepcode`。",
          "",
          "### 2. 配置 provider — **必做**",
          "",
          "```bash",
          "deepcode provider",
          "```",
          "",
          "配置 LLM 连接与模型。",
          "",
          "### 3. 无 key 快速验证 — **必做**",
          "",
          "```bash",
          "deepcode exec '简单任务'",
          "```",
          "",
          "看报错定位；或 `deepcode --help` 确认命令面。"
        ],
        "怎么确认修好了": [
          "",
          "命令面正常（实测输出形态）：",
          "",
          "```text",
          "deepcode --version → DeepCode 2.1.0",
          "deepcode --help → 命令面完整",
          "```",
          "",
          "配置 provider 后任务能跑通（需 LLM key，本市场未实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 未 init | `~/.deepcode` 不存在，先 init |",
          "| 未配 provider | agent 链路空转，配好 key |",
          "| 无 key 定位 | `deepcode exec '简单任务'` 看报错 |",
          "| 配置目录 | `~/.deepcode` 可重定位 `$DEEPCODE_HOME`，卸载不自动删除 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`deepcode` 启动后交互无响应，或 agent 任务报错；`deepcode loop/goal` 不工作。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "首次使用需要初始化与 LLM 配置：`deepcode init`（生成 ~/.deepcode）+ `deepcode provider` 配置模型 key；没配 LLM 时 agent 链路空转。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. `deepcode init` 初始化。",
          "2. `deepcode provider` 配置 LLM 连接与模型。",
          "3. 想无 key 快速验证：`deepcode exec '简单任务'` 看报错定位；或 `deepcode --help` 确认命令面。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测：--version/--help 正常；init/provider 属交互配置面，需 LLM key，未实测（manifest semi_auto_note 已标注）。",
          ""
        ],
        "本市场实测安装 / 卸载（2026-08-28）": [
          "",
          "安装（manifest install.cmd，隔离 venv /tmp/l34-dc/venv，Python 3.14.4）：",
          "",
          "```",
          "[install] pip install -i https://pypi.tuna.tsinghua.edu.cn/simple deepcode-hku && deepcode --version",
          "依赖：aiohttp/cryptography/pillow/mcp/mcp-server-git/openai/anthropic/google-genai 等 60+ 包",
          "DeepCode 2.1.0",
          "[install] exit=0",
          "```",
          "",
          "卸载（manifest uninstall.cmd + verify）：",
          "",
          "```",
          "[uninstall] pip uninstall -y deepcode-hku → Successfully uninstalled deepcode-hku-2.1.0",
          "[uninstall] 验证: deepcode removed: OK（fresh shell，verify exit=0）",
          "```",
          "",
          "- 完整日志：实测记录（本地留存）。",
          "- CLI 冒烟：实测记录（本地留存）（DeepCode 2.1.0；命令面完整）。",
          "- 共装实测：实测记录（本地留存）（omc/agt）+ coexist-herdr/logfire（2026-08-28）。",
          "- 未能实测：TUI 交互、MCP client 实跑、agent 任务（需 LLM key）。",
          ""
        ],
        "冲突标注": [
          "",
          "- possible-compatible（实测 CLI 面）：oh-my-claudecode / agent-governance-toolkit（实测记录（本地留存））；herdr / logfire 共装实测。",
          "- possible-compatible（推断态）：其余收录条目（矩阵对子）——deepcode 写面 = ~/.deepcode（可重定位 $DEEPCODE_HOME），无端口 daemon（MCP 按需）。",
          "- 注意：配置目录 ~/.deepcode 为用户数据，卸载不自动删除。",
          ""
        ],
        "名称混淆": [
          "",
          "- 三套名字：PyPI 包 `deepcode-hku` / CLI 命令 `deepcode` / 项目名 HKUDS/DeepCode——安装/卸载用包名，日常用命令名。",
          "- 与 DeepSeek（模型）、DeepCode（其它同名代码分析工具）区分：本项目 = HKUDS/DeepCode 开源 Agentic 编码框架。",
          ""
        ],
        "判据（验证状态）": [
          "",
          "- 症状清单：",
          "  - [x] git clone 连接被重置 → 清 Git 代理/换热点（CSDN 2026-06；走 PyPI 路径规避）",
          "  - [x] curl 证书报错 → --ssl-no-revoke + 虚拟环境（CSDN 2026-01；Windows 场景未实测）",
          "  - [x] Python>=3.10（3.8 下载不了 deepcode 包）（CSDN 2025-08；3.14.4 实测）",
          "  - [x] pip 超时 → 镜像源加速（CSDN 2026-04；清华镜像实测 40.3s）",
          "  - [x] Brave MCP WinError 2 / npx 未跑起来（CSDN 2026-01；命令面确认，实跑未测）",
          "  - [x] 装完 agent 无响应 → init + provider（manifest 配置面标注）",
          "- 实测步骤：",
          "  - [x] 安装验证（manifest install.cmd：清华镜像 60+ 包 → DeepCode 2.1.0 exit 0）",
          "  - [x] 卸载验证（manifest uninstall.cmd：pip uninstall → verify 零残留 exit 0）",
          "  - [x] 网络/镜像场景（清华镜像 vs 官方 PyPI 对比；）",
          "  - [x] 未能实测项：TUI 交互、MCP client 实跑、agent 任务（需 LLM key）",
          ""
        ],
        "验收": [
          "",
          "- [x] 症状 → 原因 → 解法 → 验证证据 四段齐全（6 症状）",
          "- [x] 名称混淆已点名（包名 deepcode-hku vs 命令 deepcode；与 DeepSeek/其它 DeepCode 区分）",
          "- [x] check-words HARD 0",
          "- [x] validate 通过",
          "- [x] 未碰 GitHub / 未用任何账号"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/deepcode-6",
        "agent": "https://trove.xin/agent/problems/deepcode-6"
      }
    },
    {
      "id": "doc-culture-consensus",
      "title": "写文档到底图什么？——从\"写文档累、看文档更累\"到\"文档是给未来自己、队友和 AI 的资产\"",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个每个写代码的人都逃不掉的循环：需求评审会上，产品经理说\"补个文档吧\"，你嘴上答应，心里想的全是\"代码都写完了还要文档干什么\"。于是文档拖到交付前一天，花了三个小时把 README、接口说明、部署步骤\"编\"了出来——写的时候你就在想，这玩意儿写完大概没人会看。果然，三个月后你自己翻它，发现部署步骤已经过时了，接口文档里有一个字段的格式跟代码对不上。你一边改一边骂：写文档累，看文档更累。",
          "",
          "然后是你的 AI 同事带来的新版本焦虑：你把项目丢给 Claude Code 或 Cursor，让它改一个功能，它生成了一坨\"看起来合理但完全不符合项目风格\"的代码——用了项目里根本不存在的依赖、绕过了你封装的公共方法、命名风格跟整个代码库格格不入。你花在审查和重构上的时间，比你自己写还多。你开始怀疑：是 AI 不行，还是我喂给它的东西不行？",
          "",
          "再讲一个团队版噩梦。你入职一家公司，leader 说\"系统是老张写的，你以后维护\"，你去找老张，老张正忙着，说\"你看代码就行，代码里都有\"。你打开代码：没有 README，没有架构图，启动依赖三个环境变量两个中间件，其中一个是老张的同事小李离职前配的，小李的交接文档是一句话\"按我之前的邮件来\"，邮件已经找不到了。你问了三个人，得到三个版本的答案，谁也不敢保证哪个是对的——最后你只能靠\"试\"来验证。",
          "",
          "而老张呢？他一个人知道所有事，他休假的时候，全组都不敢动那个系统。",
          "",
          "如果你也在经历这些——文档写了没人看、AI 生成代码质量像开盲盒、团队知识全在几个人脑子里、新人来了三个月还不敢动代码——这篇文章是写给同类人的。它不是教你背模板，是那些真被\"文档债\"教育过的人用加班和返工换来的共识：**文档从来不是给公司看的交付物，是给未来的自己、给队友、给 AI 看的资产。谁先把这件事想明白，谁就少踩三年的坑。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：文档为什么越写越没用？四个错位，每个都是\"看着在写，实际在空转\"。",
          "",
          "**第一，把\"写文档\"当成\"完成任务\"。** 大多数团队的文档是被\"补\"出来的：需求评审要文档、交付要文档、年终要文档，于是文档成了\"交付物\"——写的人想着赶紧交差，看的人想着赶紧翻过去。文档一旦成了形式，就没人维护；没人维护，就没人看；没人看，就更没人写。这个恶性循环的起点，就是把文档当\"任务\"而不是\"资产\"。",
          "",
          "**第二，把文档定位成\"给领导看\"而不是\"给未来的自己看\"。** 有个很扎心的共识：写文档最大的受益者，其实是三个月后的自己。你写的部署步骤、接口说明、踩坑记录，真正在看的人是你自己——但大多数人在写的时候，脑子里想的是\"评审会不会通过\"，而不是\"三个月后的我翻到这一页，能不能 30 秒搞明白\"。定位错了，写法就错了：要么堆砌官话，要么省略关键细节。",
          "",
          "**第三，把\"同步\"当成唯一沟通方式。** 很多团队的信息传递靠\"开会 + 口头 + 私聊\"：讨论在会议室里发生，结论在口头里流传，细节在私聊里丢失。新同事想知道\"这个模块为什么这么设计\"，只能找当事人问——当事人忙、当事人离职、当事人自己也忘了。同步沟通是即时的、易失的；文档是异步的、可检索的。把知识只放在同步通道里，等于把知识放在流沙上。",
          "",
          "**第四，把文档和代码当成两套世界。** 代码改了，文档没改；文档写了，代码没写。文档一旦和代码脱节，就失去了\"可信度\"——看文档的人发现文档是错的，从此再也不看文档。而 AI 时代这个错位的代价被放大了：AI 读不到你的口头知识，它只能读到文档、代码和注释。你文档里没有的上下文，AI 就替你\"猜\"——猜出来的代码质量，自然像开盲盒。",
          "",
          "下面每条经验，都是把文档从\"形式主义\"拉回\"资产\"的做法——它们不是一家之言，是几套独立实践反复验证过的共识。",
          ""
        ],
        "解法": [
          "",
          "### 经验 1：文档的本质是\"异步协作的输入\"——先写文档，再开会",
          "",
          "现象：字节跳动被反复当作协同标杆研究，其内部沟通体系的核心不是会议，是飞书文档。任何重要讨论或会议之前，发起人必须写一份结构清晰的文档：背景、方案、待决议项。开会时大家先默读文档（飞阅会），再针对文档上的评论讨论——不搞\"背景介绍会\"。这看起来是流程繁琐，实际是效率革命：写文档强迫作者把问题想清楚，读文档让所有人在同一信息起点上讨论，评论和修改历史让决策过程本身被沉淀下来。",
          "",
          "做法：①**文档先行**——重要讨论/方案评审前，先花 20 分钟把背景、方案、待决议项写成半页文档，再拉人开会；②**会议读文档**——会议开始先静默读文档，口头只讨论\"差异和决策\"，不重复背景；③**结论入文档**——会上达成的结论当场写进文档，而不是散落在会议纪要/聊天记录里。",
          "",
          "为什么：书面化、结构化地表达，天然逼你把\"感觉\"变成\"判断\"，把\"大概\"变成\"具体\"。口头沟通是\"高带宽但易失\"的，文档是\"低带宽但持久\"的——异步协作的价值不是取代沟通，是让沟通有据可查、可回放、可复用。",
          "",
          "### 经验 2：AI 时代，文档就是喂给 AI 的\"超级 Prompt\"——Garbage In, Garbage Out",
          "",
          "现象：很多人抱怨 AI 生成代码质量不稳：上一个需求它写得优雅高效，下一个需求它产出一段有潜在安全漏洞的代码。深入看会发现，根因往往不是模型能力不足，而是\"垃圾进、垃圾出\"——AI 编程助手像一位技艺高超但对项目背景一无所知的顶级程序员，你的输入越模糊、越缺上下文，它的产出越偏离预期。",
          "",
          "传统文档是\"写给人看\"的（PRD、架构文档、数据库设计），AI 时代这些文档被重新定义：它们是喂给 AI 编程工具的结构化上下文。",
          "",
          "给它一套完整文档（需求 + 架构 + 数据模型 + 前端规范），它生成代码的质量会质变；只给它一句\"开发一个电商 App\"，它就替你猜。",
          "",
          "做法：①**把项目骨架写成文档**——README 里写清楚\"这个项目是什么、技术栈、目录结构、启动方式、约定\"，这不仅是给人看的，也是给 AI 的初始上下文；②**关键决策写\"为什么\"**——在文档/注释里写\"为什么这么设计\"，AI 才能理解约束而不是瞎猜；③**用文档驱动 AI**——给 AI 任务前，先确认它能看到 README、接口文档、现有代码风格；",
          "",
          "④**AI 生成文档，文档再驱动 AI**——用 AI 起草文档（PRD/接口文档/变更说明），人来审校，再拿文档去驱动 AI 写代码，形成闭环。",
          "",
          "为什么：AI 的输出质量上限，取决于你给它的上下文质量下限。文档从\"写给人看的指南\"变成\"喂给 AI 的超级 Prompt\"之后，写文档不再是\"额外负担\"，而是让 AI 替你干活的前提投资——这笔账怎么算都是赚的。",
          "",
          "### 经验 3：知识孤岛和单点故障，只能靠文档化知识来破——部落知识要变成文档知识",
          "",
          "现象：很多团队的真实状态是\"知识在人脑里\"：某个系统只有老张会维护，某个配置只有小李知道，新人来了只能挨个问人。这就是典型的\"部落知识\"——存在于个人脑子里的未文档化知识。部落知识的代价是：单点故障（老张休假全组停摆）、信息孤岛（各部门各搞一套）、信息偏差（三个人三个版本）。更隐蔽的是\"闹鬼墓地\"现象：代码没人敢动，因为懂它的人不在了，注释写着\"别删，删了会炸\"。",
          "",
          "做法：①**关键知识必须落文档**——系统的部署方式、环境变量、设计决策、踩坑记录，凡是\"只有某个人知道\"的知识，都要变成文档；②**文档和部落知识互补**——文档是骨架，专家是血肉：文档回答\"是什么/怎么做\"，专家回答\"为什么是这样/特殊情况怎么办\"；③**建立规范的\"单一信息源\"**——开发者指南、代码实践规范、常见问题手册，由主题专家维护和评审，让团队遇到问题先查文档而不是先问人；",
          "",
          "④**新知识及时沉淀**——你解决了一个没人遇到过的问题，把它写成文档/笔记，而不是只在自己脑子里留个印象。",
          "",
          "为什么：软件工程的核心是人，但知识不能只存在人身上。文档化的知识可以在团队甚至整个组织内继承和流动——它是唯一不随人员流动而流失的载体。团队的知识管理目标不是\"消灭专家\"，是\"让专家知道的东西，不再只有专家知道\"。",
          "",
          "### 经验 4：写文档是工程纪律，不是时间浪费——正规研发流程里，30% 时间写文档是正常的",
          "",
          "现象：有篇流传很广的《给浮躁的软件业同仁》讲了一个真实案例：作者团队去印度做外包项目，对方需求分析阶段提交了一份四十多页的 SRS 文档，多人合写但风格一致、内容详实；项目推进中每个阶段都有 review，QA 拿着 checklist 参会打勾；对方按\"300 行/人月\"的生产率排期——作者心里暗想\"我们六七百行\"，对方却说\"这个计划是建立在确保质量的基础上的\"。",
          "",
          "作者反思：我们缺的不是代码能力，是工程纪律。",
          "",
          "文章里有一句话被反复引用：**\"作为代码程序员，30% 的工作时间写技术文档是很正常的，高级程序员和系统分析员的比例还要更高。缺乏文档，一个软件系统就缺乏生命力，在未来的查错、升级以及模块复用时会遇到极大的麻烦。\"**",
          "",
          "做法：①**把写文档算进工时**——需求分析、设计、编码、测试、文档，文档是开发流程的一环，不是编码的附属品；②**先写文档再写代码**——重要模块先写设计文档（目标/方案/接口/风险），写清楚再动手，返工率会显著下降；③**文档要过 review**——像代码评审一样评审文档：风格一致、内容准确、覆盖完整；",
          "",
          "④**别把\"写文档\"和\"写代码\"对立**——文档不是从编码时间里克扣的，它是让编码不返工的投资。",
          "",
          "为什么：文档的质量直接决定系统的生命力——查错、升级、复用，全靠文档撑着。把文档当\"编码之外的事\"，文档就会变成\"编码之后补的事\"；把文档当工程纪律，文档就会变成\"让代码少返工的事\"。印度项目的启示不是流程繁琐，是：文档写得好，需求分析阶段就把 80% 的问题消灭了。",
          "",
          "### 经验 5：让文档\"活着\"——更新是纪律，流转是机制，可读性是底线",
          "",
          "现象：文档最大的死因不是没人写，是没人更新。代码改了文档没改，读者上过一次当就再也不信文档；更常见的是文档写了一堆，但没人知道它在哪、没人知道该看哪份，最后文档沦为\"知识坟场\"。还有一层：文档写出来要让人（和 AI）读得懂——写文档的人熟悉上下文，读文档的人没有，于是\"文档写了等于没写\"。",
          "",
          "做法：①**发现错误马上改**——看到文档有错误或遗漏，立刻更新，不要等\"统一整理\"；②**新流程新步骤必须文档化**——你创建了一个新的开发步骤，就用文档记录它，并让团队容易找到；③**建立反馈机制**——文档要有\"哪里错了\"的反馈通道，读者能报告，维护者能响应；④**用代码评审传播规范**——代码评审是\"可读性流程\"：它把最佳实践以书面意见的形式传播，融合文档知识和专家经验的优点；",
          "",
          "⑤**推广文档而不是重复讲解**——有人问问题，先引导他看文档，再在需要时补充讲解——既沉淀知识，又避免知识回流到个人脑子。",
          "",
          "为什么：文档的敌人是\"过期\"和\"找不到\"。更新纪律解决过期，流转机制解决找不到，可读性解决\"写了等于没写\"。这三件事做齐，文档才会从\"死资产\"变成\"活资产\"——它会自己替你回答问题，替你的团队省时间，替 AI 提供上下文。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 本周给手头项目补一页\"启动文档\"：这个项目是什么、技术栈、怎么跑起来、目录结构、部署方式——写给自己看，30 分钟以内完成；",
          "2. 下一个要开的方案会，先写半页\"背景 + 方案 + 待决议项\"再拉人，会上先读文档再讨论；",
          "3. 今天起，AI 改代码前先喂上下文：把 README、关键接口、代码风格约定丢给它，再让它干活——对比一下生成质量；",
          "4. 你解决的第一个\"没人遇到过的问题\"，写一条笔记/文档存进项目，不要只留在脑子里；",
          "5. 看到文档有错，当场改掉，不攒着。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目**：把\"写给未来的自己\"当第一原则——README 写清楚怎么跑、为什么这么设计、踩过什么坑；三个月后你会感谢现在的自己；AI 改代码前先给它 README 和关键文件的路径，生成质量会明显提升。",
          "- **小团队（5-15 人）**：先落地三件事——文档先行（讨论前写半页）、单一信息源（部署/环境变量/踩坑放一个固定地方）、发现错误马上改；每周花 10 分钟维护文档，别让它烂尾。",
          "- **大型团队**：文档要\"当产品做\"——规范信息源 + 主题专家评审 + 反馈闭环 + 可检索；代码评审承担\"可读性流程\"的职责；飞阅会式的\"读文档再讨论\"可以消灭大部分无效会议。",
          "- **AI 时代**：文档从\"给人看\"变成\"人机共用\"——AI 读文档、生成代码、起草文档，人负责审校和维护；\"上下文管理\"是 AI 编程时代的新核心竞争力，而上下文管理的起点就是文档；AI 生成代码质量不稳时，先检查你喂给它的上下文够不够，而不是先怪 AI。",
          "- **如果你正卡在\"文档没人看 / 知识全靠问人 / AI 生成代码像开盲盒\"**：从最小的闭环开始——给手头项目补一页启动文档；下次 AI 干活前先喂 README；解决一个新问题就写一条笔记。三个星期后，你会看到差别：最明显的是，你不需要再向三个月前的自己\"考古\"了。",
          "",
          "收尾：写文档最反直觉的一点：它不省现在的你，省三个月后的你。我栽过的坑是项目只有我一个人懂，休假回来连自己都看不懂自己的代码；后来把 README 当「写给未来自己的信」——怎么跑、为什么这么设计、踩过什么坑——一切才开始不一样。别把「文档」想得太宏大，就写一件事：未来某个时刻的你，需要知道什么。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/doc-culture-consensus",
        "agent": "https://trove.xin/agent/problems/doc-culture-consensus"
      }
    },
    {
      "id": "docker-build-cache",
      "title": "Docker build 缓存不生效（每次改代码都重跑安装层）—— COPY 顺序与 .dockerignore",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/43073137/why-is-copy-in-docker-build-not-detecting-updates",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "只改了一个业务文件，npm install / pip install 整层却每次都重跑：",
          "",
          "```",
          "Step 3/4 : COPY . /app",
          "Step 4/4 : RUN npm install        # 每次构建都执行，几分钟的重装",
          "```",
          "",
          "构建越来越慢，CI 上表现更明显（每次都全量重装）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker 构建缓存的匹配是\"父链 + 指令 + 输入\"：",
          "",
          "- 每一层缓存的 key 包含父层 ID、指令和该指令的输入（COPY/ADD 的校验和、RUN 的命令串）；",
          "- `COPY . .` 把整个构建上下文都算进输入——**改一个文件 = COPY 层校验和变化 = 该层及之后所有层缓存全部失效**；",
          "- `RUN npm install` 本身命令串没变，但它的父层（COPY 层）变了，所以也无法命中缓存；",
          "- 构建上下文里塞了不该塞的东西（node_modules、.git、缓存目录）会让 COPY 层频繁失效。",
          "",
          "本质：依赖安装层被\"不应该影响它\"的业务文件变更拖下水。修复方向 = 让 COPY 层只包含安装层真正需要的输入（依赖清单），并把安装层放在前面。",
          "",
          "缓存失效的传导路径是「输入变化 → 层校验和变化 → 父链断裂」：Docker 对每一层只记录父层 ID、指令和输入摘要，`COPY . .` 的输入是整个构建上下文，业务文件一动 COPY 层摘要就变；`RUN npm install` 的缓存 key 里带着父层 ID，父层一变它跟着失效——缓存不是「命令相同就复用」，是「整条父链逐层校验」。",
          "",
          "三个确定性推论：①依赖安装层必须放在 COPY 业务代码之前，让它只依赖依赖清单（package.json/lock）的摘要；②`.dockerignore` 收紧上下文 = 减少无关文件抖动摘要的概率，也减小上传体积；③调试时看 `---> Using cache` 首次消失的位置，那处就是父链断裂点，往它前面找输入变化。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：把依赖安装挪到 COPY 之前（npm 例：先 COPY 依赖清单，再 RUN 安装，最后 COPY 全量）",
          "# Dockerfile 里：COPY package.json package-lock.json ./ && RUN npm ci 放在 COPY . /app 之前",
          "```",
          "",
          "pip 同理：`COPY requirements.txt ./` → `RUN pip install -r requirements.txt`；go：`COPY go.mod go.sum ./` → `RUN go mod download`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 把依赖安装挪到 COPY 之前 — **必做**",
          "",
          "```dockerfile",
          "COPY package.json package-lock.json ./",
          "RUN npm ci",
          "COPY . /app",
          "```",
          "",
          "只有依赖清单变化才会重跑安装层，改业务代码不再触发。",
          "",
          "### 2. 用 .dockerignore 收紧上下文 — **必做**",
          "",
          "排除 node_modules/.git/构建产物/缓存目录等，否则 `COPY . .` 的校验和会随无关文件抖动（也减小上传体积）。",
          "",
          "### 3. 调试时看缓存标记 — **必做**",
          "",
          "build 输出里 `---> Using cache` 出现的位置就是命中的层；改文件后如果 COPY 层没有 `Using cache`，它之后的所有层都会重跑——顺着输出往前找第一处失效点。",
          "",
          "### 4. 需要跨层共享的缓存（BuildKit） — **必做**",
          "",
          "```dockerfile",
          "RUN --mount=type=cache,target=/root/.npm npm ci",
          "```",
          "",
          "把包管理器缓存挂成独立缓存卷，网络层命中率更高。",
          "",
          "### 5. CI 里用 --cache-from — **必做**",
          "",
          "推送远端镜像做缓存源（buildx `--cache-from`），配合\"安装前置\"结构效果最好；顺序反了的话 `--cache-from` 也救不了频繁失效的安装层。"
        ],
        "怎么确认修好了": [
          "",
          "改业务文件后安装层不再重跑（实测输出形态）：",
          "",
          "```text",
          "再次改 app.cfg 重建 → Step 3/4 RUN -- Using cache（安装层不重跑，修复生效）",
          "```",
          "",
          "修复前：改文件后 COPY 与 RUN 均无 `Using cache`（症状复现）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| npm | `COPY package.json package-lock.json ./` → `RUN npm ci` |",
          "| pip | `COPY requirements.txt ./` → `RUN pip install -r requirements.txt` |",
          "| go | `COPY go.mod go.sum ./` → `RUN go mod download` |",
          "| 构建上下文 | .dockerignore 排除 node_modules/.git/缓存 |",
          "| BuildKit | `--mount=type=cache` 独立缓存卷 |",
          "| CI | `--cache-from` + 安装前置结构 |",
          ""
        ],
        "症状": [
          "",
          "构建镜像时缓存\"总是失效\"：",
          "",
          "```",
          "# 只改了一个业务文件，npm install / pip install 整层却每次都重跑",
          "Step 3/4 : COPY . /app",
          "Step 4/4 : RUN npm install        # 每次构建都执行，几分钟的重装",
          "```",
          "",
          "特征：Dockerfile 结构是 `COPY . .` 在 `RUN npm install`（或 pip/go mod）之前；",
          "随便改一个源文件，COPY 层变了，后面整个依赖安装层跟着失效重跑；",
          "构建越来越慢，且 CI 上表现更明显（每次都全量重装）。",
          ""
        ],
        "原因": [
          "",
          "Docker 构建缓存的匹配是\"父链 + 指令 + 输入\"：",
          "- 每一层缓存的 key 包含父层 ID、指令和该指令的输入（COPY/ADD 的校验和、RUN 的命令串）；",
          "- `COPY . .` 把整个构建上下文（包括任何改动的业务文件）都算进输入——改一个文件 = COPY 层校验和变化 = 该层及之后所有层缓存全部失效；",
          "- `RUN npm install` 本身命令串没变，但它的父层（COPY 层）变了，所以它也无法命中缓存；",
          "- 同理，构建上下文里塞了不该塞的东西（node_modules、.git、缓存目录）会让 COPY 层频繁失效。",
          "本质：依赖安装层被\"不应该影响它\"的业务文件变更拖下水。修复方向 = 让 COPY 层只包含安装层真正需要的输入（依赖清单），并把安装层放在前面。",
          ""
        ],
        "解法": [
          "",
          "1. **把依赖安装挪到 COPY 之前**：先 `COPY package.json /app/` + `RUN npm install`，再 `COPY . /app`——",
          "   只有 package.json 变化才会重跑安装层，改业务代码不再触发。",
          "   - npm: `COPY package.json package-lock.json ./` → `RUN npm ci`；",
          "   - pip: `COPY requirements.txt ./` → `RUN pip install -r requirements.txt`；",
          "   - go: `COPY go.mod go.sum ./` → `RUN go mod download`。",
          "2. **用 .dockerignore 收紧上下文**：排除 node_modules/.git/构建产物/缓存目录等，",
          "   否则 `COPY . .` 的校验和会随无关文件抖动（也减小上传体积）。",
          "3. **调试时看缓存标记**：build 输出里 `---> Using cache` 出现的位置就是命中的层；",
          "   改文件后如果 COPY 层没有 `Using cache`，它之后的所有层都会重跑——顺着输出往前找第一处失效点。",
          "4. **需要跨层共享的缓存**（BuildKit）：`RUN --mount=type=cache,target=/root/.npm` 等，",
          "   把包管理器缓存挂成独立缓存卷，装依赖时网络层命中率更高。",
          "5. **CI 里用 --cache-from**：推送远端镜像做缓存源（buildx `--cache-from`），配合上面的\"安装前置\"结构效果最好；",
          "   顺序反了的话，--cache-from 也救不了频繁失效的安装层。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，Docker 29.1.3 legacy builder，alpine:3.20 纯本地无网络）：",
          "  - 问题结构：`COPY . /app` + `RUN echo install-deps > /installed.txt`（RUN 命令串全程不变）；",
          "  - ① 首次构建：四步全部执行；",
          "  - ② 改 app.cfg 后重建：`Step 2/4 WORKDIR -- Using cache`，但 `COPY . /app` 与 `RUN` 均无 `Using cache`——安装层被业务文件变更拖着重跑（症状复现）；",
          "  - ③ 修复结构：`RUN echo install-deps > /installed.txt` 前置 + `COPY app.cfg /app/app.cfg` 后置，首次重建 RUN 执行一次（层链变化）；",
          "  - ④ 再次改 app.cfg 重建：`Step 3/4 RUN -- Using cache`，安装层不再重跑（修复生效）；",
          "  - 清理：测试镜像已删除。",
          "- 参考案例：[SO #43073137](https://stackoverflow.com/q/43073137)（COPY 不检测更新/缓存不生效）+ [farscapian/lnplay#35](https://github.com/farscapian/lnplay/issues/35)（COPY 全部源码在 RUN yarn 之上导致安装层失效）同款。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：build 依赖 daemon）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-build-cache",
        "agent": "https://trove.xin/agent/problems/docker-build-cache"
      }
    },
    {
      "id": "docker-compose-command-not-found",
      "title": "docker-compose / docker compose 报 command not found（Compose 没装或没接线）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本站实测沉淀（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "两种典型形态：WSL 里 `docker-compose --version` 报 `The command 'docker-compose' could not be found in this WSL 2 distro`（Docker Desktop 的 shim 在提示），或 `docker compose` 报 `docker: unknown command: docker compose`。",
          "",
          "compose 不是 docker CLI 内置子命令，而是**独立插件**——装了 docker 不等于装了 compose。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker CLI 的插件机制：`docker <子命令>` 会去 `~/.docker/cli-站内文档 和 `/usr/libexec/docker/cli-站内文档 找名为 `docker-<子命令>` 的可执行文件。compose 就是 `docker-compose` 插件：没装 = `unknown command`。",
          "",
          "而 WSL 里那条「could not be found in this WSL 2 distro」是 Docker Desktop 装在 Windows PATH 里的 shim 在说话——它检测到当前发行版没激活 Desktop 集成、也没装原生插件。",
          "",
          "`docker compose` = docker CLI 发现并执行 `docker-compose` 插件二进制；`docker-compose`（独立命令） = 直接执行同名单二进制。两条路最终都要求 compose 二进制存在于 CLI 插件目录或 PATH。",
          "",
          "Ubuntu 的 `docker-compose-v2` 包安装后放到 `/usr/libexec/docker/cli-站内文档，`docker compose` 立即可用；",
          "",
          "而 PATH 里残留的 Docker Desktop shim（`/mnt/c/Program Files/Docker/Docker/resources/bin/`）在 WSL 里被优先命中时，会先检查 WSL 集成再决定报什么错。",
          "",
          "确定性推论：先分清「谁在回答」——报 `unknown command: docker compose` = 插件目录里没这东西，装 `docker-compose-v2` 或下载官方插件二进制到 `~/.docker/cli-站内文档；",
          "",
          "报 `could not be found in this WSL 2 distro` = Desktop shim 优先，二选一：启用 Desktop 的 WSL 集成，或装原生插件让 shim 让路。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# Ubuntu/Debian：装 compose 插件（实测 2.40.3）",
          "sudo apt-get install -y docker-compose-v2 && docker compose version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 docker 本体在 — **必做**",
          "",
          "```bash",
          "docker --version",
          "```",
          "",
          "### 2. 按报错形态分流 — **按诊断结果**",
          "",
          "- `docker: unknown command: docker compose` → 装插件：`sudo apt-get install -y docker-compose-v2`（Debian/Ubuntu）或下载官方插件二进制到 `~/.docker/cli-站内文档（其它发行版，见 Docker 官方 Compose 安装文档）。",
          "- `The command 'docker-compose' could not be found in this WSL 2 distro` → 这是 Desktop shim：要么在 Docker Desktop 设置里打开该发行版的 WSL 集成（用 Desktop 的 compose），要么装原生插件后用 `type docker-compose` 确认 shim 不再抢先。",
          "",
          "### 3. 验证接线成功 — **必做**",
          "",
          "```bash",
          "docker compose version",
          "# 期望输出：Docker Compose version v2.x.x（实测 2.40.3）",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker compose version` 输出版本号且 exit 0；在含 `docker-compose.yml` 的目录里 `docker compose config` 能解析出服务列表。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Ubuntu/Debian | `apt-get install -y docker-compose-v2`（实测成功） |",
          "| Docker Desktop 集成 | 设置 → Resources → WSL integration 打开对应发行版 |",
          "| 其它发行版 | 官方插件二进制放 `~/.docker/cli-站内文档（或 `/usr/local/lib/docker/cli-站内文档） |",
          "| 旧版 `docker-compose` | `pip install docker-compose`（v1，已停止维护，优先 v2） |",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 形态 1：Desktop shim 在 WSL 里",
          "The command 'docker-compose' could not be found in this WSL 2 distro.",
          "We recommend to activate the WSL integration in Docker Desktop settings.",
          "",
          "# 形态 2：原生 CLI 没插件",
          "docker: unknown command: docker compose",
          "",
          "Run 'docker --help' for more information",
          "```",
          "",
          "特征：`docker` 本体命令正常，只有 compose 相关命令报错；形态 2 是「CLI 在，插件不在」，形态 1 是「Windows 侧 shim 在，WSL 集成/原生插件不在」。",
          ""
        ],
        "原因": [
          "",
          "compose 是 docker CLI 的插件（`docker-compose` 二进制放在 CLI 插件目录），不是 docker 本体的一部分。没装插件 = `unknown command`；WSL 里 Desktop shim 命中时给「激活 WSL 集成」的引导文案。修复本质是把 compose 插件装到 CLI 能发现的位置。",
          ""
        ],
        "解法": [
          "",
          "1. 原生：`sudo apt-get install -y docker-compose-v2`（Debian/Ubuntu），或官方插件二进制放 `~/.docker/cli-站内文档；",
          "2. Desktop 用户：设置里开 WSL 集成；",
          "3. 验证：`docker compose version`。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-31，WSL Ubuntu，docker CLI 29.1.3）：",
          "  - 链 A（故障）：`docker-compose --version` → `The command 'docker-compose' could not be found in this WSL 2 distro...`（Desktop shim 文案）；`docker compose --help` → `docker: unknown command: docker compose`；",
          "  - 链 B（修复）：`apt-get install -y docker-compose-v2`（2.40.3+ds1-0ubuntu1，archive.ubuntu.com）→ `docker compose version` → `Docker Compose version 2.40.3+ds1-0ubuntu1`（exit 0）。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（compose 连的是同一个 daemon socket）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-compose-command-not-found",
        "agent": "https://trove.xin/agent/problems/docker-compose-command-not-found"
      }
    },
    {
      "id": "docker-compose-depends-not-ready",
      "title": "docker compose 起栈失败——依赖服务没就绪（Connection refused / unhealthy / depends_on 不生效）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本机实测复现（2026-08-31，Docker 域缺口 #17）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "`docker compose up` 后，依赖方（app）报 `Connection refused`（连数据库/上游服务失败）、`dependency failed to start: container xxx is unhealthy`，或者 `docker compose ps` 里服务一直显示 `unhealthy`。",
          "",
          "网上教程都写了 `depends_on`，你也写了，还是不行——**普通 `depends_on` 只保证启动顺序，不保证依赖就绪**：db 容器起来了 ≠ 端口能连。",
          "",
          "> 本文路径：**快速修复**（一段 compose 片段）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "compose 的 `depends_on` 有两种语义：**启动顺序**（短写法 `depends_on: [db]`：先创建/先启动 db，不等就绪）和**就绪条件**（长写法 `depends_on: db: condition: service_healthy`：等 db 的健康检查通过才启动 app）。",
          "",
          "数据库/中间件从「进程启动」到「端口可连」通常要几秒（实测：本机 python http.server 模拟的 db 要 sleep 4 秒），这期间连接必然被拒。",
          "",
          "就绪条件依赖**健康检查**（`healthcheck`）：compose 按 `interval` 周期执行 `test` 命令，连续失败 `retries` 次判 `unhealthy`，成功即 `healthy`。`up --wait` 会等所有服务 healthy（可配 `--wait-timeout` 防止无限等）。",
          "",
          "健康检查有三个高频坑，全部实测复现：① `test` 用了镜像里没有的二进制（如 alpine 没有 `busybox httpd` applet）→ 容器直接 `exit 127`，compose 报 `dependency failed to start ... exited (127)`；",
          "",
          "② 服务启动慢但没设 `start_period` → 启动期连续失败被误判 `unhealthy`（实测：服务 4 秒才就绪，interval 1s × retries 3 在 ~3 秒时已判死）；",
          "",
          "③ 检查命令本身写错/永远失败 → `ps` 显示 `Up x seconds (unhealthy)`、`up --wait` 超时报错。",
          "",
          "确定性推论：看到 unhealthy 先看 `docker inspect <容器> --format \"{{json .State.Health.Log}}\"` 的最后一条输出——它直接告诉你检查命令为什么失败；看到 exited (127) 则先确认 test 命令在镜像里存在。",
          ""
        ],
        "快速修复": [
          "",
          "```yaml",
          "# 给依赖服务加 healthcheck，app 用长写法 depends_on：",
          "services:",
          "  db:",
          "    image: your-db-image",
          "    healthcheck:",
          "      test: [\"CMD\", \"your-healthcheck-cmd\"]   # 必须在镜像里存在！",
          "      interval: 5s",
          "      timeout: 3s",
          "      retries: 5",
          "      start_period: 10s      # 启动期失败不计入 retries（服务启动慢必配）",
          "  app:",
          "    image: your-app-image",
          "    depends_on:",
          "      db:",
          "        condition: service_healthy   # 等 db 健康才启动 app",
          "```",
          "",
          "```bash",
          "docker compose up -d",
          "docker compose up --wait --wait-timeout 60   # 验收：全部 healthy 才算成功",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先分清报错类型 — **必做**",
          "",
          "- `Connection refused`：app 启动太早，依赖还没就绪 → 上健康检查 + `condition: service_healthy`。",
          "- `dependency failed to start ... is unhealthy`：健康检查失败 → 看第 2 步。",
          "- `dependency failed to start ... exited (127)`：健康检查命令在镜像里不存在 → 换命令或换镜像。",
          "",
          "### 2. 查健康检查为什么失败 — **必做**",
          "",
          "```bash",
          "docker compose ps                      # 看哪个服务 unhealthy",
          "docker inspect <容器名> --format \"{{json .State.Health.Log}}\" | tail -c 800",
          "```",
          "",
          "最后一条 log 的 `Output` 就是检查命令的真实报错（实测能看到 urllib 的 ConnectionRefused traceback）。",
          "",
          "### 3. 检查 test 命令在镜像里存在 — **必做**",
          "",
          "```bash",
          "docker run --rm <镜像> sh -c \"which <命令>\"    # 不存在 → 换命令",
          "```",
          "",
          "实测反例：alpine 里 `busybox httpd` 不存在（`httpd: applet not found`，exit 127）。",
          "",
          "### 4. 启动慢的服务补 start_period — **按诊断**",
          "",
          "服务要几秒才就绪（拉配置、等端口）时，`start_period` 必须大于就绪时间，否则启动期失败会计入 retries 被误判 unhealthy（实测：无 start_period 时 4 秒就绪的服务在 ~3 秒被判死）。",
          "",
          "### 5. 用 up --wait 验收 — **必做**",
          "",
          "```bash",
          "docker compose up -d",
          "docker compose up --wait --wait-timeout 60    # 超时即失败，不无限等",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker compose up --wait --wait-timeout 60` 退出码 0；`docker compose ps` 全部 `Up x seconds (healthy)`；app 日志不再有 Connection refused，业务请求成功（实测：db healthy 后 app 立即 `APP_OK`）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 写法 | 行为 |",
          "|---|---|",
          "| `depends_on: [db]` | 只保证启动顺序；app 可能先于 db 就绪启动 → Connection refused |",
          "| `depends_on: db: condition: service_healthy` | 等 db 健康检查通过才启动 app（推荐） |",
          "| 健康检查命令不存在 | 容器 exit 127 → `dependency failed to start ... exited (127)` |",
          "| 启动慢 + 无 start_period | 启动期失败计入 retries → 误判 unhealthy |",
          "| 健康检查永远失败 | `ps` 显示 unhealthy；`up --wait` 超时报 `dependency failed to start ... is unhealthy` |",
          ""
        ],
        "症状": [
          "",
          "```",
          "app-1  | wget: can't connect to remote host (172.18.0.3): Connection refused",
          "app-1  | APP_CONN_REFUSED",
          "```",
          "",
          "```",
          "db-1  | httpd: applet not found",
          "db-1 exited with code 127",
          "dependency failed to start: container depstest-db-1 exited (127)",
          "```",
          "",
          "```",
          "Container depstest-db-1  Waiting",
          "Container depstest-db-1  Error",
          "dependency failed to start: container depstest-db-1 is unhealthy",
          "```",
          "",
          "特征：compose 里写了 depends_on 仍然连接被拒；或 ps 显示 unhealthy；或容器 exit 127。",
          ""
        ],
        "原因": [
          "",
          "普通 `depends_on` 只保证启动顺序不保证就绪；就绪需 `healthcheck` + `condition: service_healthy`。健康检查失败三来源：命令在镜像中不存在（exit 127）、启动慢没配 start_period（误判 unhealthy）、检查命令自身错误（持续 unhealthy）。",
          ""
        ],
        "解法": [
          "",
          "1. 依赖服务加 `healthcheck`（test 命令必须存在于镜像；启动慢加 `start_period`）；",
          "2. app 改用 `depends_on: db: condition: service_healthy`；",
          "3. `docker compose up -d` 后 `docker compose up --wait --wait-timeout 60` 验收；",
          "4. unhealthy 时 `docker inspect <容器> --format \"{{json .State.Health.Log}}\"` 看检查命令真实报错。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测复现（2026-08-31，WSL 定制预构建版，docker 29.1.3 + compose 2.40.3）：",
          "  - 相位1：普通 depends_on → app 立即启动 → `APP_CONN_REFUSED`（db sleep 4s）；",
          "  - 相位2：healthcheck + `condition: service_healthy` + start_period 6s → db healthy 后 app 启动 → `APP_OK`；",
          "  - 变体A：无 start_period → 启动期 3 次失败 → `dependency failed to start ... is unhealthy`（服务 4s 后才就绪）；",
          "  - 变体B：db 用 `busybox httpd`（alpine 无此 applet）→ `httpd: applet not found`、exit 127；",
          "  - 相位3：healthcheck `[\"CMD\",\"false\"]` → ps 显示 `Up 7 seconds (unhealthy)`、`up --wait` 报 unhealthy 错误。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [docker compose / docker compose command not found](docker-compose-command-not-found.html)（同域：compose 启动链路的前置问题）",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（起栈前 daemon 必须活着，连接错误先于 compose 语义）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-compose-depends-not-ready",
        "agent": "https://trove.xin/agent/problems/docker-compose-depends-not-ready"
      }
    },
    {
      "id": "docker-container-dns",
      "title": "Docker 容器内域名解析失败（wget: bad address / Temporary failure in name resolution / Could not resolve host）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/docker/for-win/issues/13808",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "容器里访问域名报解析失败（`wget: bad address 'github.com'` / `curl: (6) Could not resolve host` / `Temporary failure in name resolution`），但**宿主上同样域名正常、IP 直连也通**。常出现在给容器配过 `--dns`、或换了网络环境（校园网/公司网/代理）之后。",
          "",
          "最经典的坑：网上教程让你配 `--dns 8.8.8.8`，而多数国内网络对 8.8.8.8 的 UDP 53 不友好。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "容器用自己的 `/etc/resolv.conf` 解析。默认 docker 继承宿主的 nameserver 生成一份；但一旦用 `docker run --dns`、compose 的 `dns:`、或 daemon.json 的 `\"dns\"` 覆盖，容器就只问指定的 DNS。该 DNS 不可达、或域名只在特定 DNS 有记录（内网域名）→ 解析失败。",
          "",
          "- 错误文案取决于程序：busybox 报 `bad address`，glibc 程序报 `Temporary failure in name resolution`——同一问题不同长相。",
          "- **IP 直连能通、域名不行** = DNS 层问题，先分层再动手。",
          "",
          "> 实测环境：WSL Ubuntu，docker 29.1.3，alpine:3.20，校园网无代理，2026-08-29。",
          "",
          "容器没有自己的 DNS 服务，它只是按 `/etc/resolv.conf` 把解析请求转发给 nameserver——这份配置默认由 docker 继承宿主生成，但一旦 `--dns`/compose `dns:`/daemon.json 覆盖，就变成「只问指定的服务器」。",
          "",
          "DNS 走 UDP 53 明文请求，国内网络对 8.8.8.8 丢包是常见现实，「网上教程的万能 DNS」在你这儿可能恰恰是断点。",
          "",
          "确定性判断路径：IP 直连通 + 域名不通 = 问题在解析链路；容器内 `cat /etc/resolv.conf` 看 nameserver 是谁；内网域名必须用内网/VPN 分配的 DNS——公共 DNS 上根本没有这条记录，换多少公共 DNS 都白换。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：用国内公共 DNS 重跑容器",
          "docker run --rm --dns 223.5.5.5 --dns 119.29.29.29 <镜像> wget -qO- http://example.com",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 分层确认 — **必做**",
          "",
          "宿主 `getent hosts github.com` 正常 → 容器内看解析配置：",
          "",
          "```bash",
          "docker run --rm <镜像> cat /etc/resolv.conf",
          "```",
          "",
          "nameserver 是被覆盖过的（可能带 `Overrides: [nameservers]`）→ 问题在指定 DNS。",
          "",
          "### 2. 单容器指定可用 DNS — **必做**",
          "",
          "```bash",
          "docker run --dns 223.5.5.5 --dns 119.29.29.29 ...",
          "```",
          "",
          "223.5.5.5（阿里）/ 119.29.29.29（腾讯）国内公共 DNS，实测可用。",
          "",
          "### 3. compose 场景 — **必做**",
          "",
          "```yaml",
          "services:",
          "  app:",
          "    dns:",
          "      - 223.5.5.5",
          "      - 119.29.29.29",
          "```",
          "",
          "### 4. 全局默认（影响所有容器） — **必做**",
          "",
          "daemon.json 加 `\"dns\": [\"223.5.5.5\", \"119.29.29.29\"]` 后重启 daemon。",
          "",
          "### 5. 配 8.8.8.8 前先测 — **必做**",
          "",
          "```bash",
          "timeout 5 bash -c 'echo > /dev/tcp/8.8.8.8/53' && echo reachable",
          "```",
          "",
          "通了才用；不通换国内公共 DNS。内网域名解析失败时，把内网/VPN 分配的 DNS 加进 `--dns`，必要时与公共 DNS 并列。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "docker run --rm --dns 223.5.5.5 <镜像> wget -qO- http://github.com >/dev/null && echo OK",
          "```",
          "",
          "不再报解析失败即成功。实测：默认继承宿主 DNS → wget 成功（exit 0）；`--dns 192.0.2.1`（必然不可达）→ `bad address`（exit 1）；`--dns 8.8.8.8` 校园网可达 → 成功；`--dns 223.5.5.5` → 成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 校园网/家宽 | 8.8.8.8 可能不可达，用 223.5.5.5 / 119.29.29.29 |",
          "| 公司内网域名 | 加内网/VPN DNS，必要时与公共 DNS 并列 |",
          "| WSL + Docker Desktop | 升级后偶发，同样按上面分层排查 |",
          "| compose | 服务级 `dns:` 配置 |",
          ""
        ],
        "症状": [
          "",
          "容器内访问域名报解析失败（宿主上同样域名正常）：",
          "",
          "```",
          "wget: bad address 'github.com'                 # busybox/alpine wget",
          "curl: (6) Could not resolve host: github.com    # curl",
          "Temporary failure in name resolution            # ping / apt-get 等 glibc 程序",
          "```",
          "",
          "特征：**IP 直连能通、域名不行**（`wget http://<IP>/` 成功证明是 DNS 层）；宿主解析正常、容器内失败；常出现在给容器配过 `--dns`、或换了网络环境（校园网/公司网/代理）之后；WSL + Docker Desktop 升级后也可能偶发（for-win#13808）。",
          ""
        ],
        "原因": [
          "",
          "容器用自己的 `/etc/resolv.conf` 做解析。默认 docker 会继承宿主的 nameserver 生成一份；但一旦用 `docker run --dns <ip>`、compose 的 `dns:`、或 daemon.json 的 `\"dns\"` 覆盖，容器就只问指定的 DNS。若该 DNS 不可达（国内网络配 `8.8.8.8` 是最经典坑：多数国内网络 UDP 53 被墙/丢包）、或域名本身只在特定 DNS 有记录（内网域名），解析就失败。错误文案取决于程序：busybox 报 `bad address`，glibc 程序报 `Temporary failure in name resolution`，同一问题不同长相。",
          ""
        ],
        "解法": [
          "",
          "1. **先分层**：宿主 `getent hosts github.com` 正常 → 容器内 `cat /etc/resolv.conf` 看 nameserver 是什么（被覆盖过的会显示 `Overrides: [nameservers]`）。",
          "2. **单容器指定可用 DNS（最常用）**：`docker run --dns 223.5.5.5 ...`（阿里）或 `--dns 119.29.29.29`（腾讯）——国内公共 DNS，实测可用。",
          "3. **compose**：服务下加 `dns: [223.5.5.5, 119.29.29.29]`。",
          "4. **全局默认**：daemon.json 加 `\"dns\": [\"223.5.5.5\", \"119.29.29.29\"]` 后重启 daemon（影响所有容器，改前确认）。",
          "5. **配 8.8.8.8 前先测**（国内网络差异大）：`timeout 5 bash -c 'echo > /dev/tcp/8.8.8.8/53'` 通了才用；不通就换国内公共 DNS。",
          "6. **内网域名**：容器解析公司/校园内网域名失败时，把内网 DNS（或 VPN 分配的 DNS）加进 `--dns`，必要时与公共 DNS 并列。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker 29.1.3，alpine:3.20，校园网无代理）：",
          "  - 基线：默认 DNS 继承宿主（resolv.conf 显示 nameserver 223.5.5.5 / 119.29.29.29），容器内 wget github.com 成功（exit 0）；",
          "  - 复现：`--dns 192.0.2.1`（保留地址必然不可达）→ 容器内 `wget: bad address 'github.com'`（exit 1），resolv.conf 显示 `nameserver 192.0.2.1` + `Overrides: [nameservers]`；",
          "  - 经典坑实测：校园网 `8.8.8.8:53` 可达（`/dev/tcp` 探测通）→ `--dns 8.8.8.8` 解析成功（exit 0）——国内网络差异大，配前必须测；",
          "  - 修复：`--dns 223.5.5.5` → wget github.com 成功（exit 0）。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- [只有个别网站打不开（其他网站正常）——分层定位](site-open-fail-others-ok.html)（相邻：单域名失败，同一套分层法）",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：容器能跑 = daemon 正常）",
          "",
          "- **同根（同一根因）**：",
          "  - [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（同根：no such host 同为 DNS 层，宿主 vs 容器内）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-container-dns",
        "agent": "https://trove.xin/agent/problems/docker-container-dns"
      }
    },
    {
      "id": "docker-container-name-conflict",
      "title": "docker 报 Conflict. The container name \"/xxx\" is already in use（容器名被占用）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本站实测沉淀（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "同一个名字的容器第二次 `docker run --name xxx ...` 时报 `docker: Error response from daemon: Conflict. The container name \"/xxx\" is already in use by container \"...\"`。",
          "",
          "脚本重跑、CI 重试、照着教程起服务最常见——**容器名是 daemon 全局唯一资源**：`docker stop` 之后名字还占着，而且 `docker ps`（不带 `-a`）根本看不到它，容易误判「名字空闲」。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`--name` 在容器 **create** 时把名字登记到 daemon 的全局名字索引；`docker stop` 只把容器状态改为 exited，容器对象和它的名字都还在；只有 `docker rm` 才删除容器对象、释放名字。所以「第二次跑同一个命令」必然撞名。compose 不报这个错，是因为它重建前自己先移除旧容器。",
          "",
          "容器生命周期是 create → start → stop → rm：名字在 create 时占用、rm 时释放，stop 不释放。报错里 `by container \"一串id\"` 就是当前占名者，`docker ps -a` 一定能查到它——`docker ps` 只显示运行中容器，退出容器必须加 `-a`。",
          "",
          "确定性推论：要么 `docker rm -f` 删掉占名者，要么换个 `--name`，没有第三条路；脚本/CI 里「先删后建」是幂等标准写法。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：删掉占名的旧容器（已退出的也能删），再重新跑原命令",
          "docker rm -f repro-conflict && docker run -d --name repro-conflict alpine sleep 60",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 找到占名者 — **必做**",
          "",
          "```bash",
          "docker ps -a | grep <名字>",
          "```",
          "",
          "已退出容器 `docker ps` 看不到，必须 `-a`。",
          "",
          "### 2. 确认它没用了再删 — **必做**",
          "",
          "```bash",
          "docker rm -f <名字>    # 正在运行的容器用 -f 强删；没在跑的直接 rm",
          "```",
          "",
          "### 3. 旧容器还有用就换名 — **按场景**",
          "",
          "```bash",
          "docker rename <旧名> <新名>        # 或新容器用另一个 --name",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker ps -a | grep <名字>` 无同名容器；重新 `docker run --name <名字>` 成功返回容器 id，且无 Conflict 报错。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 已退出容器占名 | 只有 `docker ps -a` 看得到（实测：stop 后名字仍占用） |",
          "| 正在运行占名 | `docker rm -f` 强删，或先 stop 再 rm |",
          "| compose 场景 | 正常不会报；若报 = 有手工 run 的同名容器 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "docker: Error response from daemon: Conflict. The container name \"/repro-conflict\" is already in use by container \"49e3c7e4...\". You have to remove (or rename) that container to be able to reuse that name.",
          "",
          "Run 'docker run --help' for more information",
          "```",
          "",
          "特征：`docker run --name` 第二次用同一个名字；报错里带完整的 `Conflict. The container name \"...\" is already in use` 原文和占名容器 id。",
          ""
        ],
        "原因": [
          "",
          "容器名是 daemon 全局唯一资源，create 时登记、`rm` 时才释放，`stop` 不释放。同名二次创建直接 Conflict；`docker ps` 只显示运行中容器，容易误判「名字空闲」。",
          ""
        ],
        "解法": [
          "",
          "1. `docker ps -a | grep <名字>` 找占名者；",
          "2. 没用了就 `docker rm -f <名字>`；还有用就 `docker rename` 或换新名字；",
          "3. 脚本/CI 养成习惯：先 `docker rm -f` 再 `docker run --name`（幂等），或加 `|| true` 容错。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-31，WSL Ubuntu，docker 29.1.3，alpine:latest）：",
          "  - 链 A：`docker run -d --name repro-conflict alpine sleep 60` 成功（输出容器 id）→ 同命令再跑 → `docker: Error response from daemon: Conflict. The container name \"/repro-conflict\" is already in use by container \"49e3c7e4...\"`（exit 125）；",
          "  - 链 B：`docker rm -f repro-conflict` 后同命令再跑 → 成功。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（撞名前先确认 daemon 活着）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-container-name-conflict",
        "agent": "https://trove.xin/agent/problems/docker-container-name-conflict"
      }
    },
    {
      "id": "docker-container-oom-killed",
      "title": "容器进程突然被杀（OOMKilled / exit code 137）——内存限制与排查",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本机实测复现（2026-08-31，Docker 域缺口 #20）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "容器里的进程跑着跑着突然没了：`docker ps -a` 显示 `Exited (137)`，`docker inspect` 里 `State.OOMKilled=true`；服务「无缘无故」重启、消失，日志里却什么都没有（进程是被内核直接 SIGKILL 的，来不及写日志）。",
          "",
          "**容器内存超过了它的 cgroup 限制，被内核 OOM killer 杀掉**——不是整机内存不足，是容器自己的配额用完了。",
          "",
          "> 本文路径：**快速修复**（三条排查命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`docker run -m 64m`（compose 的 `mem_limit` / `deploy.resources.limits.memory`）给容器设 cgroup 内存上限；",
          "",
          "进程累计内存（含 page cache 归属）超过上限时，内核 OOM killer 在**该 cgroup 内**选进程杀掉——被杀的容器 exit code = **137**（128 + SIGKILL 9）。不设 `-m` 的容器**没有内存上限**，可以吃满整台宿主机，把别的服务一起拖死。",
          "",
          "Docker 用 cgroup v2 的 `memory.max` 实现限制（实测：`-m 128m` 的容器内 `cat /sys/fs/cgroup/memory.max` = 134217728）。",
          "",
          "OOM 触发时内核记 `oom-kill:constraint=CONSTRAINT_MEMCG,...task=python3`（实测 dmesg 原文），这是「cgroup 级 OOM」的确定性证据——和整机内存不足（`constraint=CONSTRAINT_NONE`）是两类。",
          "",
          "两个默认值要记住：`--memory-swap` 默认 = `--memory` × 2（即容器最多可用 2 倍内存的 swap，实测 help 文档语义），`-1` 表示 swap 不限；`docker stats` 的 `MEM USAGE / LIMIT` 是观察入口。",
          "",
          "排查链是确定的：`docker ps -a` 看 137 → `docker inspect <容器> --format \"{{.State.OOMKilled}}\"` 确认 → `docker stats` 看实际用量 → `dmesg | grep oom` 看内核记录。",
          "",
          "确定性推论：exit 137 不一定是 OOM（也可能是手动 kill），先看 OOMKilled 字段再下结论；Java/Python 等有自己内存池的应用，容器限制要按「进程 RSS + 运行时开销」算，不是按应用堆配置算。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1. 确认是 OOM 被杀",
          "docker ps -a --filter \"status=exited\"          # Exited (137)",
          "docker inspect <容器名> --format \"{{.State.OOMKilled}} {{.State.ExitCode}}\"",
          "# true 137 = OOM；false 137 = 被人 kill -9 / 其它原因",
          "",
          "# 2. 看内存实际用量",
          "docker stats --no-stream                      # MEM USAGE / LIMIT 两列",
          "",
          "# 3. 内核侧证据（可选）",
          "dmesg | grep -i \"oom-kill\" | tail",
          "```",
          "",
          "```yaml",
          "# compose 里设限制 + 崩溃兜底重启",
          "services:",
          "  app:",
          "    image: your-image",
          "    mem_limit: 512m          # 或 deploy.resources.limits.memory: 512M",
          "    restart: unless-stopped  # OOM 被杀后自动拉起（但会反复重启 = 还是要治本）",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 OOMKilled — **必做**",
          "",
          "```bash",
          "docker inspect <容器名> --format \"{{.State.OOMKilled}} {{.State.ExitCode}}\"",
          "```",
          "",
          "`true 137` → cgroup OOM（本坑）；`false 137` → 外部 kill -9 或平台回收，另查。",
          "",
          "### 2. 量化内存用量 — **必做**",
          "",
          "```bash",
          "docker stats --no-stream <容器名>",
          "# MEM USAGE / LIMIT：用量接近 LIMIT 就是配额不够",
          "```",
          "",
          "### 3. 设/调限制 — **按诊断**",
          "",
          "- 没设 `-m`：先设一个合理上限（`-m 512m` / compose `mem_limit`），防止单容器吃满宿主；",
          "- 设了还 OOM：把限制调大，或治本（见第 4 步）；",
          "- swap：默认 `--memory-swap` = `--memory` × 2；`--memory-swap -1` 表示 swap 不限（谨慎，可能把宿主 swap 打爆）。",
          "",
          "### 4. 治本：查进程内存模型 — **按诊断**",
          "",
          "Java：`-Xmx` 只算堆，容器限制要再加元空间/线程栈/直接内存（常见方案：`-XX:MaxRAMPercentage=75`）；Python：大对象/缓存/`bytearray` 等一次性分配（实测 256MB 分配撞 64m 限制即被杀）；Node：`--max-old-space-size`。改完代码/参数后重建容器。",
          "",
          "### 5. 验证 — **必做**",
          "",
          "```bash",
          "docker run --rm -m 128m your-image your-app     # 稳定运行",
          "docker stats --no-stream                        # 用量明显低于 LIMIT",
          "docker inspect <容器名> --format \"{{.State.OOMKilled}}\"   # false",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker ps` 里容器稳定 `Up`（不再反复 Exited 137）；`docker inspect` 的 `OOMKilled=false`；`docker stats` 显示用量低于限制且有富余（实测：128m 限制、96MB 分配时 stats 显示 `100.1MiB / 128MiB 78.22%`）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 场景 | 表现 |",
          "|---|---|",
          "| 无 `-m` 限制 | 容器可吃满宿主内存，拖垮其它服务（默认无上限） |",
          "| `-m 64m` + 分配 256MB | 进程被杀：exit 137、OOMKilled=true、dmesg 记 `constraint=CONSTRAINT_MEMCG`（实测） |",
          "| `--memory-swap` | 默认 = memory × 2；`-1` = swap 不限；调大可在内存峰值期缓冲 |",
          "| exit 137 但 OOMKilled=false | 外部 kill -9（如平台回收），不是本坑 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ docker ps -a",
          "oomdemo2   ...   Exited (137) 4 seconds ago",
          "",
          "$ docker inspect oomdemo2 --format \"{{.State.OOMKilled}} {{.State.ExitCode}}\"",
          "true 137",
          "",
          "# 内核侧（dmesg 原文节选）",
          "oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=docker-...scope,task=python3,pid=24719,uid=0",
          "Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB",
          "```",
          "",
          "特征：容器 exit 137 + OOMKilled=true；应用日志里没有报错（SIGKILL 不经过应用）；`docker stats` 用量贴着 LIMIT。",
          ""
        ],
        "原因": [
          "",
          "容器进程累计内存超过 cgroup（`memory.max`）限制，内核 OOM killer 在 cgroup 内杀进程，exit code = 137（SIGKILL）。不设 `-m` 则无上限。默认 `--memory-swap` = 2×`--memory`。",
          ""
        ],
        "解法": [
          "",
          "1. `docker inspect` 确认 OOMKilled=true；",
          "2. `docker stats` 量化用量，`dmesg | grep oom-kill` 取内核证据；",
          "3. 设/调 `-m`（compose `mem_limit`），必要时调 `--memory-swap`；",
          "4. 治本：按运行时模型配（Java MaxRAMPercentage / Node max-old-space-size / Python 大分配）并重建容器；",
          "5. 验证 OOMKilled=false、stats 有富余。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测复现（2026-08-31，WSL 定制预构建版，docker 29.1.3）：",
          "  - 基线：无限制容器分配 256MB 成功（`alloc ok 268435456`）；",
          "  - `-m 64m` + 分配 256MB → 进程被杀，`docker run` 退出码 137；`docker inspect` → `OOMKilled=true ExitCode=137`；",
          "  - dmesg 原文：`oom-kill:constraint=CONSTRAINT_MEMCG,...,task=python3,pid=24719` + `Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB`；",
          "  - `-m 128m` 容器内 `cat /sys/fs/cgroup/memory.max` = 134217728（128MiB）；`docker stats` 显示 `100.1MiB / 128MiB 78.22%`；",
          "  - `--memory-swap` 默认=2×memory 语义按 docker help 文档核对。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "- [docker system prune 清理边界](docker-prune-scope.html)（同批 Docker 域资源问题）",
          "- [容器时区/中文乱码](docker-container-timezone-locale.html)（同批容器运行配置坑）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-container-oom-killed",
        "agent": "https://trove.xin/agent/problems/docker-container-oom-killed"
      }
    },
    {
      "id": "docker-container-timezone-locale",
      "title": "容器里时间差 8 小时（date 显示 UTC）/ 中文乱码（UnicodeEncodeError）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本机实测复现（2026-08-31，Docker 域缺口 #18）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "容器里 `date` 显示 `UTC`，跟宿主机差 8 小时；照着教程在 compose/`docker run` 里加了 `-e TZ=Asia/Shanghai`，重启容器后**还是 UTC**；或者程序打印中文直接报 `UnicodeEncodeError: 'ascii' codec can't encode characters`，日志里中文全是乱码/问号。",
          "",
          "**容器默认 UTC、默认 C locale，基础镜像还经常缺时区数据**——这两个坑是容器部署的高频开场白。",
          "",
          "> 本文路径：**快速修复**（两条命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker 镜像基于 Linux 的默认配置：时区 = UTC，locale = C。`date` 读的是 `/etc/localtime`（或 TZ 环境变量 + zoneinfo），不是宿主机的时区——**容器不会继承宿主机时区**。",
          "",
          "TZ 生效分两层：环境变量 `TZ` 只是告诉 libc「去 zoneinfo 找哪个时区」，如果镜像里没有 `/usr/share/zoneinfo/`（实测 alpine 基础镜像连这个目录都没有），`TZ=Asia/Shanghai` 就静默无效、仍然 UTC——这是「设了 TZ 没用」的头号原因。",
          "",
          "两个替代路径：① 装 tzdata（alpine：`apk add tzdata`；Debian 系镜像通常已自带，直接 `-e TZ=Asia/Shanghai` 就生效）；② 不依赖 zoneinfo 的 POSIX TZ 字符串（`-e TZ=UTC-8` = 东八区，**符号与直觉相反**，实测 23:09 与宿主机 CST 一致）。",
          "",
          "中文乱码同理分两层：容器内程序按 ASCII/系统编码输出（Python 在 C locale 下默认自动 UTF-8——PEP 540，实测能正常打印中文；",
          "",
          "但被 `PYTHONIOENCODING=ascii` 强制后立刻 `UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)`，实测原文）；",
          "",
          "以及查看终端编码不匹配（容器输出 UTF-8 字节，Windows 控制台按 GBK 解码 → 显示乱码，与容器无关）。确定性推论：TZ 不生效先 `docker run --rm <镜像> ls /usr/share/zoneinfo` 看有没有时区数据；UnicodeEncodeError 先看环境变量里有没有 `PYTHONIOENCODING`/`LANG` 把编码锁死。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 时区：Debian 系/python 系镜像直接生效；alpine 无 tzdata 时用 POSIX 写法",
          "docker run -e TZ=Asia/Shanghai your-image date          # 镜像自带 zoneinfo 时",
          "docker run -e TZ=UTC-8 your-image date                  # 无 tzdata 的镜像（UTC-8 = 东八区，符号相反）",
          "",
          "# 中文乱码：容器内程序强制 UTF-8 输出",
          "docker run -e PYTHONUTF8=1 -e PYTHONIOENCODING=utf-8 your-python-image ...",
          "```",
          "",
          "```yaml",
          "# compose 里同样写法",
          "services:",
          "  app:",
          "    image: your-image",
          "    environment:",
          "      - TZ=Asia/Shanghai",
          "      - PYTHONUTF8=1",
          "      - PYTHONIOENCODING=utf-8",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认时区差在哪 — **必做**",
          "",
          "```bash",
          "docker run --rm alpine date          # UTC = 没生效",
          "docker run --rm alpine ls /usr/share/zoneinfo   # No such file = 镜像没 tzdata",
          "```",
          "",
          "### 2. 按镜像类型选修复 — **按诊断**",
          "",
          "- Debian 系（ubuntu/debian/python:slim 等）：自带 zoneinfo，`-e TZ=Asia/Shanghai` 直接生效；",
          "- alpine 系：先 `apk add --no-cache tzdata`（Dockerfile 里装好，重构建），再 `-e TZ=Asia/Shanghai`；不想装就 `-e TZ=UTC-8`（POSIX，符号相反）；",
          "- 已有容器：改环境变量后必须**重建容器**（`docker compose up -d --force-recreate` 或重新 `docker run`）；`docker restart` 用的是创建时的 env，改 env 不生效。",
          "",
          "### 3. 排查中文乱码 — **按诊断**",
          "",
          "```bash",
          "# 容器内确认 Python 实际输出编码",
          "docker run --rm your-python-image python3 -c \"import sys; print(sys.stdout.encoding)\"",
          "# utf-8 = 正常；ascii = 被环境变量锁死",
          "```",
          "",
          "修法优先级：`PYTHONUTF8=1`（全局）→ `PYTHONIOENCODING=utf-8`（stdout/stderr）→ `LANG=C.UTF-8`（locale）。注意 `PYTHONUTF8=1` **不覆盖**显式 `PYTHONIOENCODING=ascii`（实测仍报错），必须把后者直接改成 `utf-8`。",
          "",
          "### 4. 查看侧乱码（日志/终端） — **按场景**",
          "",
          "容器输出没问题但 Windows 终端/旧编辑器显示乱码：把终端切到 UTF-8（PowerShell：`chcp 65001`；VS Code 右下角选 UTF-8），这是显示层问题，改容器没用。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker run --rm -e TZ=Asia/Shanghai <镜像> date` 与宿主机 `date` 小时一致；`python3 -c \"import sys; print(sys.stdout.encoding)\"` 输出 `utf-8`；带中文的程序输出正常显示。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 表现 |",
          "|---|---|",
          "| Debian 系镜像 | 自带 zoneinfo，`-e TZ=Asia/Shanghai` 直接生效 |",
          "| alpine 基础镜像 | 无 `/usr/share/zoneinfo`，`TZ=Asia/Shanghai` 静默无效（实测） |",
          "| 无 tzdata 临时方案 | `-e TZ=UTC-8`（POSIX，符号相反=东八区，实测有效） |",
          "| Python 默认 C locale | PEP 540 自动 UTF-8，中文可打印（实测） |",
          "| PYTHONIOENCODING=ascii | UnicodeEncodeError（实测原文） |",
          "| Windows 控制台看 UTF-8 输出 | GBK 解码显示乱码（显示层，非容器问题） |",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 时区",
          "$ docker run --rm -e TZ=Asia/Shanghai alpine date",
          "Mon Aug 31 15:09:18 UTC 2026        # 还是 UTC！",
          "$ docker run --rm alpine ls /usr/share/zoneinfo",
          "ls: /usr/share/zoneinfo: No such file or directory",
          "",
          "# 乱码",
          "$ docker run --rm -e PYTHONIOENCODING=ascii python:3.12-alpine python3 -c \"print('中文测试')\"",
          "UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)",
          "```",
          "",
          "特征：`TZ=Asia/Shanghai` 设了但 date 仍 UTC；镜像缺 zoneinfo；程序输出中文抛 ascii 编码错误。",
          ""
        ],
        "原因": [
          "",
          "容器默认 UTC 且不继承宿主机时区；alpine 等基础镜像缺 `/usr/share/zoneinfo`，`TZ=Asia/Shanghai` 无效。中文乱码=容器内程序被锁成 ASCII/非 UTF-8 输出（UnicodeEncodeError），或查看终端编码与 UTF-8 不匹配（显示乱码）。",
          ""
        ],
        "解法": [
          "",
          "1. 时区：Debian 系 `-e TZ=Asia/Shanghai`；alpine 装 tzdata 或临时 `-e TZ=UTC-8`（POSIX 符号相反）；改 env 后重建容器；",
          "2. 乱码：`PYTHONUTF8=1` / `PYTHONIOENCODING=utf-8` / `LANG=C.UTF-8`；显式 ascii 必须直接改成 utf-8；",
          "3. 查看侧：终端切 UTF-8（`chcp 65001` 等）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测复现（2026-08-31，WSL 定制预构建版，docker 29.1.3）：",
          "  - 宿主机 `23:09:18 CST` vs 容器默认 `15:09:18 UTC`（差 8h）；",
          "  - `-e TZ=Asia/Shanghai` alpine → 仍 UTC；`ls /usr/share/zoneinfo` → No such file；",
          "  - `-e TZ=UTC-8` alpine → `23:09:19`（与 CST 一致，POSIX 符号相反）；",
          "  - python 默认 `stdout encoding: utf-8` 中文正常；`PYTHONIOENCODING=ascii` → UnicodeEncodeError 原文；`PYTHONIOENCODING=utf-8` / `PYTHONUTF8=1` → 正常；",
          "  - 环境限制：本机 alpinelinux 源异常（`apk add tzdata` 报 no such package），标准 tzdata 路径按官方文档记录，未在本机完整验证。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [PowerShell 调 wsl 输出乱码](wsl-pipe-encoding.html)（同为字符编码显示问题族）",
          "  - [docker compose 起栈失败：依赖没就绪](docker-compose-depends-not-ready.html)（容器运行配置同批实测 canonical）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-container-timezone-locale",
        "agent": "https://trove.xin/agent/problems/docker-container-timezone-locale"
      }
    },
    {
      "id": "docker-daemon-connect",
      "title": "docker 命令报 Cannot connect to the Docker daemon at unix:///var/run/docker.sock / failed to connect to the docker API（连不上 daemon）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/docker/for-win/issues/14759",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`docker ps` 一执行就报 `Cannot connect to the Docker daemon at unix:///var/run/docker.sock`（老版文案）或 `failed to connect to the docker API at unix:///tmp/no-such.sock`（新版文案）。CLI 明明在，报的是**连接**问题。",
          "",
          "WSL 里装过 Docker Desktop 又用原生 docker 的机器最容易出现——多半是 `DOCKER_HOST` 残留把 CLI 指到了不存在的 socket。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "docker CLI 通过 unix socket 与 daemon 通信（默认 `/var/run/docker.sock`）。连不上的三大根因：",
          "",
          "| 根因 | 特征 |",
          "|---|---|",
          "| daemon 没在跑 | socket 文件不存在；原生 docker 需 `systemctl start docker`，Desktop 需启动应用 |",
          "| socket 路径不对 | `DOCKER_HOST` 或 context 指向不存在的路径——WSL 最常见：残留 Docker Desktop 的 `DOCKER_HOST=unix:///mnt/wsl/docker-desktop/shared-sockets/.../docker.proxy.sock` |",
          "| 权限不足 | socket 存在但 `permission denied`，用户不在 docker 组 |",
          "",
          "**报错里的路径是关键线索**：指向 `/var/run/docker.sock`（默认）还是别处（被 DOCKER_HOST/context 改过）。",
          "",
          "> 实测环境：WSL Ubuntu，docker CLI 29.1.3 / daemon 29.1.3，2026-08-29。",
          "",
          "docker CLI 与 daemon 是「客户端-服务器」架构：CLI 本身不做任何容器操作，一切请求都通过 unix socket 走 HTTP 风格 API，所以报错永远是「连不上」，而不是「命令不存在」。",
          "",
          "CLI 找 daemon 的寻址顺序是 `DOCKER_HOST` 环境变量 → 当前 context 的 endpoint → 默认 `/var/run/docker.sock`；残留的 `DOCKER_HOST`（比如 Docker Desktop 的 WSL socket 路径）会把 CLI 指到一个不存在的文件。",
          "",
          "确定性推论：报错里写明的 socket 路径就是「CLI 实际去找的地方」，先看路径再动手——指向默认路径 = daemon 没起或权限问题；指向别处 = 寻址被环境变量/context 改过，`unset DOCKER_HOST` 或 `docker context use default` 恢复。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：清掉残留的 DOCKER_HOST 再试",
          "unset DOCKER_HOST && docker ps",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 看报错路径 + 查环境变量 — **排查**",
          "",
          "```bash",
          "env | grep -i docker_host",
          "```",
          "",
          "有输出 → `unset DOCKER_HOST`（或 `export -n DOCKER_HOST`）再试。实测：本机残留 `DOCKER_HOST` 指向 Desktop socket（文件不存在），unset 后 `docker ps` 恢复——daemon 其实一直在跑。",
          "",
          "### 2. 看 context — **必做**",
          "",
          "```bash",
          "docker context ls",
          "```",
          "",
          "看当前 `*` 的 endpoint；误配就 `docker context use default`。",
          "",
          "### 3. socket 不存在 = daemon 没起 — **必做**",
          "",
          "```bash",
          "ls -l /var/run/docker.sock",
          "```",
          "",
          "不存在 → `sudo systemctl start docker`（或 `sudo service docker start`）。存在但 `permission denied` → 用户不在 docker 组：`sudo usermod -aG docker $USER` 后重新登录，或临时 `sudo docker ...`。",
          "",
          "### 4. WSL + Docker Desktop 混用 — **必做**",
          "",
          "二选一：用 Desktop 就保持 `DOCKER_HOST` 指向它的 socket（或 `docker context use desktop-linux`）；用原生 daemon 就清掉 Desktop 相关环境变量/context，别让两个入口打架。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "docker ps",
          "```",
          "",
          "正常返回容器列表即恢复（实测：unset DOCKER_HOST 后恢复；复现时 `DOCKER_HOST=unix:///tmp/no-such-docker.sock docker ps` 精确复现报错，exit 1）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 原生 docker（systemd） | `sudo systemctl start docker` / `service docker start` |",
          "| Docker Desktop | 启动应用 + WSL 集成；用 desktop-linux context |",
          "| WSL 混用残留 | `unset DOCKER_HOST` + `docker context use default` |",
          "| 非 root 用户 | `sudo usermod -aG docker $USER`，重登生效 |",
          ""
        ],
        "症状": [
          "",
          "`docker ps` / `docker run` 等命令一执行就报连不上 daemon，两种文案（版本不同）：",
          "",
          "```",
          "# 老版 CLI 文案",
          "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?",
          "",
          "# 新版 CLI 文案（docker 25+）",
          "failed to connect to the docker API at unix:///tmp/no-such.sock; check if the path is correct",
          "and if the daemon is running: dial unix /tmp/no-such.sock: connect: no such file or directory",
          "```",
          "",
          "特征：**CLI 能跑、报错的是连接**（不是 command not found）；报错里的路径是关键线索——指向 `/var/run/docker.sock`（默认）还是别处（说明被 DOCKER_HOST/context 改过）；WSL 里装过 Docker Desktop 又用原生 docker 的机器最容易出现。",
          ""
        ],
        "原因": [
          "",
          "docker CLI 通过 unix socket 与 daemon 通信（默认 `/var/run/docker.sock`）。连不上的三大根因：",
          "",
          "1. **daemon 没在跑**：原生 docker 需 `systemctl start docker`（或 `sudo service docker start`）；Docker Desktop 需启动应用/WSL 集成；",
          "2. **socket 路径不对**：`DOCKER_HOST` 环境变量或 `docker context` 指向了不存在的 socket——WSL 里最常见：环境里残留 `DOCKER_HOST=unix:///mnt/wsl/docker-desktop/shared-sockets/.../docker.proxy.sock`（Docker Desktop 路径），但实际用原生 daemon；或 Desktop 升级后 socket 路径变化；",
          "3. **权限不足**：socket 属主 root:docker（`srw-rw----`），非 root 用户不在 docker 组时 `permission denied`（报错是 connect: permission denied）。",
          ""
        ],
        "解法": [
          "",
          "1. **看报错里的路径**：如果路径不是 `/var/run/docker.sock`，先查环境变量：`env | grep -i docker_host`——有就 `unset DOCKER_HOST`（或 `export -n DOCKER_HOST`）再试。",
          "2. **看 context**：`docker context ls`，看当前 `*` 的 endpoint 是什么；误配就 `docker context use default`。",
          "3. **确认 socket 与 daemon**：`ls -l /var/run/docker.sock` 不存在 = daemon 没起（`sudo systemctl start docker` / `sudo service docker start`）；存在但报 permission denied = 用户不在 docker 组（`sudo usermod -aG docker $USER` 后重新登录，或临时 `sudo docker ...`）。",
          "4. **WSL + Docker Desktop 混用**：二选一——用 Desktop 就保持 `DOCKER_HOST` 指向它的 socket（或用 `docker context use desktop-linux`）；用原生 daemon 就清掉 Desktop 相关环境变量/context，别让两个入口打架。",
          "5. **验证**：`docker ps` 正常返回即恢复。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker CLI 29.1.3 / daemon 29.1.3）：",
          "  - 基线：`/var/run/docker.sock` 存在（srw-rw---- root docker），docker ps 正常；",
          "  - 复现 1：`DOCKER_HOST=unix:///tmp/no-such-docker.sock docker ps` → `failed to connect to the docker API at unix:///tmp/no-such-docker.sock ... dial unix ... no such file or directory`（exit 1）；",
          "  - 复现 1b：`DOCKER_HOST` 指向 Docker Desktop 的 socket 路径（本机真实残留值 `/mnt/wsl/docker-desktop/shared-sockets/guest-services/docker.proxy.sock`）→ 该文件不存在 → 同样报错——即本机此前「docker 全挂」的真实原因；",
          "  - 排查：`docker context ls` 显示 default（unix:///var/run/docker.sock）与 desktop-linux 两个 context；`env | grep -i DOCKER_HOST` 定位污染源；",
          "  - 修复：unset DOCKER_HOST 后 docker ps 恢复（daemon 其实一直在跑）。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（前置：daemon 没起，pull 一样报连接错）",
          "  - [端口占用 port is already allocated](docker-port-already-allocated.html)（前置：能报端口占用 = daemon 已正常）",
          "  - [docker exec 报 not a TTY](docker-exec-tty.html)（前置：exec 也走 daemon API）",
          "  - [Docker build 缓存不生效](docker-build-cache.html)（前置：build 也依赖 daemon）",
          "  - [挂载目录 Permission denied](docker-volume-permission.html)（前置：容器能启动 = daemon 正常，权限错在挂载层）",
          "",
          "- **同根（同一根因）**：",
          "  - [Docker Desktop 启动失败/孤儿 socket](docker-desktop-orphan-socket.html)（同根：Desktop 引擎/socket 层异常，「连不上 daemon」的 Desktop 场景根因）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-daemon-connect",
        "agent": "https://trove.xin/agent/problems/docker-daemon-connect"
      }
    },
    {
      "id": "docker-desktop-orphan-socket",
      "title": "Docker Desktop 启动失败：The file cannot be accessed by the system / 孤儿 AF_UNIX socket",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://github.com/docker/desktop-feedback/issues/531",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Docker Desktop 引擎起不来，GUI 弹「An unexpected error occurred」；`com.docker.backend.exe.log` 里是：",
          "",
          "```text",
          "starting services: initializing Ingest server: listening on unix://C:/Users/<user>/AppData/Local/Docker/run/sailor-ingest.sock: remove ...: The file cannot be accessed by the system",
          "```",
          "",
          "更烦的是「修好一个下一个又报」：`sailor-ingest.sock` → `docker-secrets-engine/engine.sock` → `dockerInference`，每次重启换一个 socket。常见诱因：Docker Desktop 非正常退出（强制结束进程、崩溃、断电），或 `docker desktop start` CLI 挂起后中断。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker Desktop 非正常退出后，`%LOCALAPPDATA%\\Docker\\run\\` 与 `%LOCALAPPDATA%\\docker-secrets-engine\\` 留下孤儿 AF_UNIX socket 文件，。",
          "",
          "这些文件带 **ReparsePoint 属性**，Windows 拒绝删除（error 1920: The file cannot be accessed by the system）。",
          "",
          "引擎启动时要 `remove` 这些旧 socket，删不掉就整体启动失败。",
          "",
          "| 尝试 | 结果 |",
          "|---|---|",
          "| `[System.IO.File]::Delete` / `Remove-Item -Force` | error 1920 |",
          "| `fsutil reparsepoint` / `robocopy` | 失败 |",
          "| WSL `rm -rf` | 静默失败（文件仍在） |",
          "| **重命名父目录** | 可行（官方 workaround） |",
          "",
          "> 若 `%APPDATA%\\Docker\\settings-store.json` 的 `EnableDockerAI: true`，第二次启动还会重建 `dockerInference` 并再次杀启动——先关掉。",
          "",
          "Docker Desktop 的 Windows 侧引擎用 AF_UNIX socket 文件与 WSL 侧通信，正常退出时引擎负责清理；非正常退出（断电/强杀）时清理逻辑没跑，socket 文件残留。",
          "",
          "Windows 对带 ReparsePoint 属性的文件有特殊保护（删除返回 error 1920），这是文件系统层的行为而不是 Docker 的 bug，所以普通删除 API、`Remove-Item`、WSL `rm` 全被拒。",
          "",
          "确定性推论：删不掉不是权限不够，是「重解析点」属性问题；官方 workaround「重命名父目录」绕开的是删除限制而不是文件本身；先确认 `settings-store.json` 里 `EnableDockerAI` 为 false，否则第二次启动会重建 `dockerInference` 再次杀启动。",
          ""
        ],
        "快速修复": [
          "",
          "```powershell",
          "# 1. 停掉所有 Docker 进程（退出 Docker Desktop）",
          "Get-Process | Where-Object { $_.ProcessName -like '*Docker*' } | Stop-Process -Force",
          "",
          "# 2. 不能删文件，只能重命名父目录（两个都要）",
          "Move-Item \"$env:LOCALAPPDATA\\Docker\\run\" \"$env:LOCALAPPDATA\\Docker\\run.old-<日期>\"",
          "Move-Item \"$env:LOCALAPPDATA\\docker-secrets-engine\" \"$env:LOCALAPPDATA\\docker-secrets-engine.old-<日期>\"",
          "",
          "# 3. 确认关闭 DockerAI（settings-store.json 里 EnableDockerAI=false，否则 Inference 又杀启动）",
          "",
          "# 4. 直接启动 Docker Desktop 并轮询就绪",
          "Start-Process 'C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe'",
          "docker version --format '{{.Server.Version}}'   # 循环等它出现",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看日志确认是这个坑 — **必做**",
          "",
          "`%LOCALAPPDATA%\\Docker\\log\\host\\com.docker.backend.exe.log` 搜 `The file cannot be accessed by the system` / `GET /error`。命中 → 不用再试各种删除方式，直接走重命名。",
          "",
          "### 2. 停 Docker 并重命名目录 — **必做**",
          "",
          "所有 Docker 进程退出后，把两个目录改名（**改名可行，删除永远失败**）。改名后启动时 Docker 会自动重建干净的 `run` / `docker-secrets-engine`。",
          "",
          "### 3. 关 EnableDockerAI — **必做**",
          "",
          "`%APPDATA%\\Docker\\settings-store.json`：`\"EnableDockerAI\": false`。不关的话启动时 Inference manager 会再杀一次启动。",
          "",
          "### 4. 启动并验证 — **必做**",
          "",
          "```powershell",
          "Start-Process 'C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe'",
          "docker version --format '{{.Server.Version}}'",
          "docker ps",
          "```",
          "",
          "### 5. 清理 — **必做**",
          "",
          "确认引擎正常后，旧目录可删：`wsl -d Ubuntu bash -lc \"rm -rf /mnt/c/Users/<user>/AppData/Local/Docker/run.old-* /mnt/c/Users/<user>/AppData/Local/docker-secrets-engine.old-*\"`（改名后顽固 reparse 文件也能被清掉）。"
        ],
        "怎么确认修好了": [
          "",
          "`docker version` 返回 server 版本、`docker ps` 正常列出容器；重启后再没出现 `The file cannot be accessed by the system`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| `docker desktop start` CLI | 实测挂起并留下新孤儿 socket，别用它；直接 Start-Process Docker Desktop.exe |",
          "| 连续失败 | 修好一个 socket 后下一个又报是常态（sailor → secrets-engine → dockerInference），一次把两个目录都改名 |",
          "| EnableDockerAI=true | 启动时重建 dockerInference 再杀启动，先置 false |",
          "| 未实测 | 非 Windows 平台（Docker Engine on Linux）无此问题，该坑仅限 Docker Desktop |",
          ""
        ],
        "症状": [
          "",
          "Docker Desktop 引擎启动失败，日志报 `listening on unix://...sailor-ingest.sock: remove ...: The file cannot be accessed by the system`；GUI 弹 unexpected error；修好一个 socket 后下一个又报。",
          ""
        ],
        "原因": [
          "",
          "非正常退出留下的孤儿 AF_UNIX socket 带 ReparsePoint 属性，Windows 拒绝删除（error 1920）；引擎启动时无法 remove 旧 socket → 整体失败。",
          ""
        ],
        "解法": [
          "",
          "1. 日志确认 `The file cannot be accessed by the system`；",
          "2. 停所有 Docker 进程；",
          "3. **重命名（不要删除）** `%LOCALAPPDATA%\\Docker\\run` 与 `%LOCALAPPDATA%\\docker-secrets-engine` 两个父目录；",
          "4. `settings-store.json` 置 `EnableDockerAI=false`；",
          "5. 直接启动 Docker Desktop.exe，轮询 `docker version` 就绪；",
          "6. 确认正常后清理旧目录。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-08-30，Windows 11 + Docker Desktop 4.88.1）：日志实锤 `sailor-ingest.sock ... The file cannot be accessed by the system`；删除方式全灭（error 1920 / WSL 静默失败）；改名父目录 + EnableDockerAI=false + 重启后 server=29.7.2，kind 集群全部恢复，WSL 侧 docker 可连；当日第二次复发（docker desktop start 挂起）同样流程修复成功。证据：实测复现日志（2026-08-30）。",
          "- 未实测：非 Windows 平台行为未实测（该问题官方 issue 仅报告于 Docker Desktop）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（包含：orphan socket 是「连不上 daemon」的 Desktop 场景根因）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-desktop-orphan-socket",
        "agent": "https://trove.xin/agent/problems/docker-desktop-orphan-socket"
      }
    },
    {
      "id": "docker-exec-tty",
      "title": "docker exec 报 the input device is not a TTY / container is not running / executable file not found",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/docker/compose/issues/7306",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "脚本、CI（Jenkins/GitHub Actions）或管道里跑 `docker exec -it ...` 报 `the input device is not a TTY`；同族还有 `container is not running`、`exec: \"bash\": executable file not found in $PATH`。手动在终端里跑同一命令却正常——所以总被当成玄学。",
          "",
          "其实规律很简单：**交互式命令（-t）在非终端环境里跑不了**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- `-t`（--tty）要求分配 TTY 伪终端，脚本/CI 的标准输入不是终端，docker CLI 直接拒绝启动；",
          "- `docker compose exec` 默认带 `-t`，所以 compose 场景更容易踩；",
          "- `container is not running`：对已停止容器执行 exec；",
          "- `executable file not found`：alpine 等精简镜像没有 `bash`（只有 `sh`）。",
          "",
          "> 实测环境：WSL Ubuntu，docker 29.1.3，alpine:3.20，2026-08-29。",
          "",
          "`-t` 的语义是「让容器进程认为自己在终端里」：docker CLI 会为它分配一个伪终端（PTY）。PTY 只能建立在「客户端 stdin 是终端」的前提下；脚本/CI 里 stdin 是管道或文件，没有终端可分配，CLI 在启动前就拒绝。`docker compose exec` 默认带 `-t`，所以 compose 场景更容易踩。",
          "",
          "确定性推论：报 `the input device is not a TTY` = 当前进程没有终端，去掉 `-t`（需要 stdin 时只留 `-i`）；脚本里用 `[ -t 0 ]` 检测 stdin 是否终端来决定要不要 `-t`，不要拍脑袋写死。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：非交互执行，去掉 -t",
          "docker exec <容器> sh -c \"你的命令\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 非交互命令去掉 -t — **必做**",
          "",
          "```bash",
          "docker exec <c> sh -c \"命令\"",
          "```",
          "",
          "需要 stdin 管道时只留 `-i`：",
          "",
          "```bash",
          "echo 数据 | docker exec -i <c> sh -c \"cat\"",
          "```",
          "",
          "（实测均可，exit 0。）",
          "",
          "### 2. compose 场景 — **必做**",
          "",
          "```bash",
          "docker compose exec -T <service> 命令",
          "```",
          "",
          "`-T` = 禁用 TTY。旧版 v1 可加 `export COMPOSE_INTERACTIVE_NO_CLI=1`，新版以 `-T` 为准。",
          "",
          "### 3. 容器没在跑 — **必做**",
          "",
          "```bash",
          "docker start <c>",
          "```",
          "",
          "先 `docker logs <c>` 看为什么退出（exit code 1 的容器先排查启动失败原因，别盲目 start）。",
          "",
          "### 4. 镜像里没有目标命令 — **必做**",
          "",
          "```bash",
          "docker exec <c> ls /bin /usr/bin",
          "```",
          "",
          "alpine 用 `sh`；要 bash 就换 ubuntu/debian 镜像，或 `docker exec <c> sh -c \"命令\"`。",
          "",
          "> 真终端里交互式进入容器保持原样：`docker exec -it <c> sh`，不要去掉 `-it`。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "docker exec <c> sh -c \"echo noninteractive-ok\"",
          "```",
          "",
          "正常输出即成功。实测：`echo hello | docker exec -i <c> sh -c \"cat\"` → hello（exit 0）；复现时 `docker exec -it <c> sh </dev/null` → `the input device is not a TTY`（exit 1）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 脚本/CI/管道 | 去掉 `-t`，只留 `-i`（需要 stdin 时） |",
          "| docker compose | `exec -T` 禁用 TTY |",
          "| 已停止容器 | `container is not running`，先 `docker start` / 查日志 |",
          "| alpine 精简镜像 | 没有 bash，用 `sh -c` |",
          ""
        ],
        "症状": [
          "",
          "脚本、CI（Jenkins/GitHub Actions）或管道里跑 `docker exec -it ...` / `docker compose exec ...` 报：",
          "",
          "```",
          "the input device is not a TTY",
          "```",
          "",
          "同族症状：`docker exec` 进已停止容器报 `Error response from daemon: container <id> is not running`；",
          "对 alpine 等精简镜像跑 `docker exec <c> bash` 报 `exec: \"bash\": executable file not found in $PATH`。",
          "特征：交互式命令在**没有终端**的环境里执行；手动在终端里跑同一命令却正常——所以总被当成玄学。",
          ""
        ],
        "原因": [
          "",
          "`-t`（--tty）要求分配一个 TTY 伪终端，脚本/CI 的标准输入不是终端，docker CLI 直接拒绝启动；",
          "`docker compose exec` 默认带 -t，所以 compose 场景更容易踩。",
          ""
        ],
        "解法": [
          "",
          "1. **非交互命令去掉 `-t`**：`docker exec <c> sh -c \"命令\"`；需要 stdin 管道时只留 `-i`：",
          "   `echo 数据 | docker exec -i <c> sh -c \"cat\"`（实测均可）。",
          "2. **compose 场景**：`docker compose exec -T <service> 命令`（-T = 禁用 TTY）；",
          "   旧版 v1 可加 `export COMPOSE_INTERACTIVE_NO_CLI=1`，但新版本以 `-T` 为准。",
          "3. **判断 TTY**：脚本里可用 `[ -t 0 ]` 检测 stdin 是否是终端，条件决定是否加 `-t`。",
          "4. **容器没在跑**：先 `docker start <c>` 或 `docker logs <c>` 看为什么退出（exit code 1 的容器先排查启动失败原因，别盲目 start）。",
          "5. **没有目标命令**：先 `docker exec <c> ls /bin /usr/bin` 看镜像里有什么；alpine 用 `sh`，要 bash 就换 ubuntu/debian 镜像，或 `docker exec <c> sh -c \"命令\"`。",
          "6. 交互式进入容器（真终端）：`docker exec -it <c> sh`，保持 -it 不变。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker 29.1.3，alpine:3.20）：",
          "  - 复现：`docker exec -it <c> sh </dev/null`（非 TTY）→ `the input device is not a TTY`（exit 1），与 compose#7306 同款；",
          "  - 修复 1：`echo hello | docker exec -i <c> sh -c \"cat\"` → hello（exit 0）；",
          "  - 修复 2：`docker exec <c> sh -c \"echo noninteractive-ok\"` → 正常（exit 0）；",
          "  - 形态 2：容器 stop 后 `docker exec <c> echo x` → `container ... is not running`（exit 1）；",
          "  - 形态 3：`docker exec <c> bash -c ...` → `exec: \"bash\": executable file not found in $PATH`（exit 127）；",
          "  - 清理：容器删除无残留。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：exec 报连接错先查 daemon）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-exec-tty",
        "agent": "https://trove.xin/agent/problems/docker-exec-tty"
      }
    },
    {
      "id": "docker-host-access",
      "title": "Docker 容器内连不上宿主机服务（localhost 是容器自己，不是宿主机）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/hankliu62/interview/issues/2102",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "宿主机上跑着服务（MySQL、Redis、本地 API…），容器里用 `localhost` 或 `127.0.0.1` 去连，报 `Connection refused`。宿主机上明明能访问（宿主 curl 127.0.0.1:端口 正常）；换 `host.docker.internal` 在 Linux 上也不通（`bad address`）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "容器有独立的网络命名空间：**容器内的 localhost/127.0.0.1 指容器自己**，不是宿主机。默认 bridge 网络下，容器里没有宿主机上那个服务，所以 Connection refused（不是防火墙，是地址根本指向容器自身）。",
          "",
          "`host.docker.internal` 只有 Docker Desktop（Windows/macOS）自动注入，**原生 Linux 的 Docker 默认没有**。",
          "",
          "| 地址 | Linux 默认 | 说明 |",
          "|---|---|---|",
          "| 容器内 localhost | 指容器自己 | 连不上宿主机服务 |",
          "| 172.17.0.1（docker0 网关） | ✅ | 默认网关 = 宿主机 |",
          "| host.docker.internal | ❌ 默认无 | 需 --add-host 注入 |",
          "",
          "容器是独立网络命名空间：容器内的 `localhost`/`127.0.0.1` 解析到容器自己的回环接口，宿主机上的服务根本不在这个地址空间里——`Connection refused` 不是防火墙拦截，是「该地址上没有任何进程在监听」。",
          "",
          "bridge 网络的默认网关（docker0，通常 172.17.0.1）才是宿主机的入口；`host.docker.internal` 只是 Docker Desktop 注入的 DNS 别名，原生 Linux 的 docker 没有这个条目，直接解析失败。",
          "",
          "确定性推论：容器访问宿主机 = 走网关 IP（172.17.0.1，Linux 通用）或 `--add-host host.docker.internal:host-gateway` 注入别名（配置文件友好）；且宿主服务必须监听 `0.0.0.0`，只监听 127.0.0.1 的话外部 IP 一样连不上——寻址和监听两层都要对。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：--add-host 把 host.docker.internal 映射到宿主机（推荐，配置文件友好）",
          "docker run --add-host host.docker.internal:host-gateway <镜像>",
          "```",
          "",
          "容器内 `host.docker.internal` 即可访问宿主机服务。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 宿主 IP（最通用） — **必做**",
          "",
          "bridge 网络的默认网关就是宿主机：",
          "",
          "```bash",
          "ip -4 addr show docker0",
          "```",
          "",
          "容器内访问 `http://172.17.0.1:<端口>`。注意宿主服务要监听 `0.0.0.0` 而不是只监听 `127.0.0.1`，否则外部 IP 也连不上。",
          "",
          "### 2. host.docker.internal（推荐） — **必做**",
          "",
          "```bash",
          "docker run --add-host host.docker.internal:host-gateway <镜像>",
          "```",
          "",
          "compose 里写 `extra_hosts: - \"host.docker.internal:host-gateway\"`。macOS/Windows 的 Docker Desktop 无需配置自带。",
          "",
          "### 3. --network host（最简单，仅 Linux） — **必做**",
          "",
          "```bash",
          "docker run --network host <镜像>",
          "```",
          "",
          "共享宿主机网络命名空间，容器内 localhost 就是宿主机。代价：不再有端口隔离，容器与宿主共用端口空间。"
        ],
        "怎么确认修好了": [
          "",
          "容器内访问宿主机服务成功（实测输出形态）：",
          "",
          "```text",
          "方法1：wget http://172.17.0.1:8123/ → 成功（exit 0）",
          "方法2b：--add-host 后 host.docker.internal:8123 → 成功（exit 0）",
          "方法3：--network host 后 localhost:8123 → 成功（exit 0）",
          "```",
          "",
          "复现基线：默认网络容器内 `wget http://localhost:8123/` → Connection refused（exit 1，实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 原生 Linux | host.docker.internal 默认无，需 --add-host（实测 bad address） |",
          "| Docker Desktop（Win/mac） | 自带 host.docker.internal |",
          "| 宿主服务监听 | 需 0.0.0.0，不能只 127.0.0.1 |",
          "| compose | `extra_hosts: - \"host.docker.internal:host-gateway\"` |",
          "| --network host | 仅 Linux，无端口隔离 |",
          ""
        ],
        "症状": [
          "",
          "宿主机上跑着服务（MySQL、Redis、本地 API…），容器里用 `localhost` 或 `127.0.0.1` 去连，连不上：",
          "",
          "```",
          "wget: can't connect to remote host: Connection refused",
          "# 或 mysql: connect to 127.0.0.1 ... failed (Can't connect)",
          "# 或 curl: (7) Failed to connect to localhost port 8123: Connection refused",
          "```",
          "",
          "特征：**服务在宿主机上明明能访问**（宿主 curl 127.0.0.1:端口 正常）；容器网络模式是默认 bridge；换 `host.docker.internal` 在 Linux 上也不通（解析失败 `bad address`）。",
          ""
        ],
        "原因": [
          "",
          "容器有独立的网络命名空间：**容器内的 localhost/127.0.0.1 指容器自己**，不是宿主机。默认 bridge 网络下，容器里没有你宿主机上那个服务，所以 Connection refused（不是防火墙，是地址根本指向容器自身）。`host.docker.internal` 这个主机名只有 Docker Desktop（Windows/macOS）自动注入，**原生 Linux 的 Docker 默认没有**，直接解析失败。",
          ""
        ],
        "解法": [
          "",
          "三种方式（Linux 实测均可用，按场景选）：",
          "",
          "1. **宿主 IP（最通用）**：bridge 网络的默认网关就是宿主机，容器内访问 `http://172.17.0.1:<端口>`（docker0 的 IP，可用 `ip -4 addr show docker0` 查）。注意宿主服务要监听 `0.0.0.0` 而不是只监听 `127.0.0.1`，否则外部 IP 也连不上。",
          "2. **host.docker.internal（推荐，配置文件友好）**：Linux 上用 `--add-host host.docker.internal:host-gateway` 把该主机名映射到宿主机（compose 里写 `extra_hosts:",
          "  - \"host.docker.internal:host-gateway\"`）。之后容器内 `host.docker.internal` 与 Docker Desktop 行为一致。macOS/Windows 的 Docker Desktop 无需配置自带。",
          "3. **--network host（最简单，仅 Linux）**：`docker run --network host` 共享宿主机网络命名空间，容器内 localhost 就是宿主机，直接 `http://localhost:<端口>`。代价：不再有端口隔离，容器与宿主共用端口空间。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker 29.1.3，alpine:3.20；宿主机 python3 http.server :8123 监听 0.0.0.0）：",
          "  - 复现：容器内 `wget http://localhost:8123/` → `Connection refused`（exit 1）；",
          "  - 方法1：`wget http://172.17.0.1:8123/`（docker0 宿主侧 IP）→ 成功（exit 0）；",
          "  - 方法2a：`wget http://host.docker.internal:8123/` → `bad address 'host.docker.internal:8123'`（Linux 默认无此主机名，exit 1）；",
          "  - 方法2b：`--add-host host.docker.internal:host-gateway` 后同一 URL → 成功（exit 0）；",
          "  - 方法3：`--network host` 后 `wget http://localhost:8123/` → 成功（exit 0）。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：容器能起 = daemon 正常）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-host-access",
        "agent": "https://trove.xin/agent/problems/docker-host-access"
      }
    },
    {
      "id": "docker-hub-pull-fail",
      "title": "docker pull 报 ERROR: Get \"https://registry-1.docker.io/v2/\" ... connection reset by peer / 拉取镜像失败",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "校园网/家宽（无代理）下 `docker pull` 报 `ERROR: Get \"https://registry-1.docker.io/v2/\": ... connection reset by peer`，或镜像拉不下来。你按网上老教程配加速器，结果更糟（`no such host`）——因为**网上流传的加速器列表大部分已经死了**，不能照抄。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker Hub 官方 registry（`registry-1.docker.io`）对国内网络没有稳定加速，直连经常被网络层断掉（connection reset / timeout 同族）。",
          "",
          "而加速器是第三方服务，**会死会换**：2026-08-29 实测 9 个常见加速器，只有 1panel.live（200）、daocloud（401）、xuanyuan（401）、1ms（401）、rat.dev（302）可用，dockerproxy.cn / sjtug / dockerpull.org / ustc 全部已死（000）。",
          "",
          "「网上旧列表」和「配置过时镜像站」是失败的第二大来源（DNS no such host）。",
          "",
          "> 实测环境：校园网无代理，Docker 29.1.3，2026-08-29。",
          "",
          "`docker pull` 的下载链路是「CLI → registry-1.docker.io → 各层 blob 的 CDN 重定向」：国内出口对 registry 主域名不稳定时，连接在传输层就被重置/超时，报错跟命令无关。",
          "",
          "镜像加速器本质是「第三方代理 registry」：它的可用性取决于运营者，会死、会换域名、会限流——所以「网上旧列表 + 配置过时镜像站」是第二类失败源（DNS no such host）。",
          "",
          "确定性推论：先分层——`curl -I https://registry-1.docker.io/v2/` 判断直连；探活加速器时 401/302 不等于死（可能是需要认证的正常响应），000 或 DNS no such host 才是真死；",
          "",
          "配置后必须 `docker info` 确认 Registry Mirrors 已生效，配完不验证等于没配。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：备份原配置 + 写入当前实测可用的加速器 + 重启 docker",
          "sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak-$(date +%Y%m%d) && sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'",
          "{",
          "  \"registry-mirrors\": [",
          "    \"https://docker.1panel.live\",",
          "    \"https://docker.m.daocloud.io\"",
          "  ]",
          "}",
          "EOF",
          "sudo service docker restart && docker pull hello-world",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认失败形态 — **必做**",
          "",
          "- `connection reset by peer` / `i/o timeout` 直连 registry → 网络层被断；",
          "- `no such host` / DNS 解析失败 → 配置的加速器域名已死。",
          "",
          "### 2. 写入实测可用的加速器 — **必做**",
          "",
          "```bash",
          "sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'",
          "{",
          "  \"registry-mirrors\": [",
          "    \"https://docker.1panel.live\",",
          "    \"https://docker.m.daocloud.io\"",
          "  ]",
          "}",
          "EOF",
          "```",
          "",
          "先备份原配置（上面快速修复命令已含备份）。加速器会变，配好后过段时间失效就再实测一轮（`curl -o /dev/null -s -w '%{http_code}' https://镜像域名/v2/`，401/200/302 算可用，000 算死）。",
          "",
          "### 3. 重启并验证 — **必做**",
          "",
          "```bash",
          "sudo service docker restart",
          "docker pull hello-world",
          "```",
          "",
          "`Download complete` / `Pull complete` 即成功（实测：更新配置后 pull 成功，退出码 0）。",
          "",
          "### 4. 拉取/解压阶段报错（docker ≥ 23.0） — **必做**",
          "",
          "有些镜像站能连但拉取解压失败，可启用 containerd 原生镜像存储：daemon.json 加 `\"features\": { \"containerd-snapshotter\": true }` 后重启（源自补充文章，本机未复现该分支，未实测）。",
          "",
          "### 5. 长期/企业方案 — **必做**",
          "",
          "自建 registry、走 HTTP 代理拉取后离线导入；CI 用能访问 Hub 的海外构建机。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "docker pull hello-world && docker run --rm hello-world",
          "```",
          "",
          "能正常拉取并运行即成功。实测：直连 `registry-1.docker.io:443` curl 12 秒 `Connection timed out`（exit 28）；更新 daemon.json（1panel.live + daocloud）后 `docker pull hello-world` 成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "| Docker Desktop (Windows) | 引擎配置在 `~/.docker/daemon.json`（同 registry-mirrors 字段），或在 Docker Engine 设置里填 registry-mirrors（无需改 /etc/docker/daemon.json）；改完 `docker desktop stop` 重启生效。2026-08-30 实测 4 个可用镜像（daocloud/1ms/1panel/xuanyuan）+ hello-world 拉取成功 |",
          "| 校园网/家宽（无代理） | 直连必断，配实测可用的镜像加速器 |",
          "| 公司内网 | 可能有内部 registry，优先问运维；否则同上 |",
          "| VPN/代理环境下 | 走代理仍 pull 失败：daemon 不读 shell 的 http_proxy，代理要配在 systemd drop-in 或 Docker Desktop 的 proxy 设置里才生效；该分支本机未实测（moby#48406，机制核对，与无代理直连失败互补） |",
          "| 老教程加速器 | dockerproxy.cn / sjtug / dockerpull.org / ustc 已死（实测 000），别照抄 |",
          ""
        ],
        "症状": [
          "",
          "`docker pull` 失败的常见形态：",
          "",
          "```",
          "# 形态 1：直连 Docker Hub 被断（网络层）",
          "Error response from daemon: Get \"https://registry-1.docker.io/v2/\": ... connection reset by peer",
          "# 或：卡在 \"Using default tag: latest\" 无进度 / timeout",
          "",
          "# 形态 2：配置的镜像加速器已死（DNS 层）",
          "Error response from daemon: failed to resolve reference \"docker.io/library/xxx:latest\":",
          "failed to do request: Head \"https://<镜像站>/v2/...\": dial tcp: lookup <镜像站> ...: no such host",
          "```",
          "",
          "同族但根因不同（别在本页死磕网络）：",
          "",
          "- `manifest for xxx not found` / `manifest unknown` → 镜像或标签不存在，见 [docker-manifest-not-found](docker-manifest-not-found.html)；",
          "- `no space left on device`（拉取/解压阶段）→ WSL 下常见真相是宿主盘满，见 [wsl-no-space-left](wsl-no-space-left.html)；",
          "- 拉一半断掉 / 反复 timeout：网络抖动，直接重跑 `docker pull` 即可——已下载的层按 digest 复用、自动跳过，不用清缓存也不用 rmi。",
          ""
        ],
        "原因": [
          "",
          "Docker Hub 官方 registry 对国内网络没有稳定加速，直连经常被网络层断掉；而第三方加速器会死会换，网上旧列表大部分已失效（2026-08-29 实测 9 个候选，仅 5 个可用）。",
          ""
        ],
        "解法": [
          "",
          "1. 备份 `/etc/docker/daemon.json`，写入当前实测可用的加速器（如 1panel.live / daocloud）。",
          "2. `sudo service docker restart`，`docker pull hello-world` 验证。",
          "3. 拉取/解压阶段报错（docker ≥ 23.0）：daemon.json 加 `\"features\": { \"containerd-snapshotter\": true }`（源自补充文章，未实测）。",
          "4. 长期/企业：自建 registry 或代理拉取后离线导入。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，校园网无代理，Docker 29.1.3）：",
          "  - 直连 `registry-1.docker.io:443`：curl 12 秒 `Connection timed out`（exit 28），与文章 connection reset by peer 同族（网络层被断）；",
          "  - 原 daemon.json 里配的旧加速器（中科大 ustc）：`getent hosts` DNS `no such host`——复现形态 2 的失败链（配置过时镜像站）；",
          "  - 9 个候选加速器连通性实测：1panel.live=200、daocloud=401、xuanyuan=401、1ms=401、rat.dev=302 可用；dockerproxy.cn / sjtug / dockerpull.org / ustc 全部 000 已死——证明「网上旧列表不可照抄」；",
          "  - 更新 daemon.json（1panel.live + daocloud）→ `sudo service docker restart` → `docker pull hello-world` 成功（Download complete / Pull complete，退出码 0）；",
          "  - 原配置备份保留在 `/etc/docker/daemon.json.bak-20260829`。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 本页 → 同根 / 变体族」读：",
          "",
          "- **前置（先排除）**：[Docker daemon 连不上](docker-daemon-connect.html)——daemon 没起时 pull 报的是连接错误，不是网络问题",
          "- **同根（同一网络层根因，换源家族）**：",
          "  - [GitHub 直连慢/失败](github-direct-connect-fail.html)（镜像/Release 资产下载被断）",
          "  - [pip 安装超时/慢](pip-mirror-source.html)（国内换源家族）",
          "  - [npm install 慢/挂起](npm-mirror-registry.html)（国内换源家族）",
          "  - [Go 模块拉取慢/超时](go-proxy-cn.html)（国内换源家族）",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（registry 直连不稳 → 换源）",
          "  - [容器内 DNS 解析失败](docker-container-dns.html)（no such host 同为 DNS 层：宿主 daemon 侧 vs 容器内）",
          "- **变体族（pull 失败的其他形态，根因不同）**：",
          "  - [镜像或标签不存在](docker-manifest-not-found.html)（manifest unknown / not found）",
          "  - [WSL 报 No space left](wsl-no-space-left.html)（pull 解压阶段 ENOSPC，WSL 宿主盘满）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-hub-pull-fail",
        "agent": "https://trove.xin/agent/problems/docker-hub-pull-fail"
      }
    },
    {
      "id": "docker-invalid-reference-format",
      "title": "docker 报 invalid reference format / repository name must be lowercase（镜像引用格式非法）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本站实测沉淀（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "`docker run` / `docker pull` 一执行就报 `docker: invalid reference format: repository name (library/Ubuntu) must be lowercase`，或只给一句干巴巴的 `docker: invalid reference format`。镜像明明存在、网络也通，就是起不来。",
          "",
          "命令里某个字符不合 Docker 的镜像名规则——最常见是**镜像仓库名大写**、**引号里带空格**、**变量没展开**（PowerShell 里 `${pwd}` 之类）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Docker 镜像引用（image reference）有严格语法：`[仓库/]名称[:标签]`。仓库名只允许小写字母、数字和 `-_.`，标签允许字母数字 `_` `-` `.`。**任何一个字符越界，daemon 直接拒绝解析**，报 invalid reference format。常见翻车点：",
          "",
          "| 翻车点 | 例子 | 报错 |",
          "|---|---|---|",
          "| 仓库名大写 | `docker run Ubuntu` | invalid reference format: repository name (library/Ubuntu) must be lowercase |",
          "| 值里带空格 | `docker run \"ubuntu \"`（引号里混进空格） | invalid reference format（短句） |",
          "| 变量没展开 | PowerShell `${pwd}` / 脚本里 `${TAG}` 未定义 | invalid reference format（展开后是空/非法字符） |",
          "",
          "docker CLI 收到 `docker run <image>` 后，先把 `<image>` 按 reference 语法解析成「仓库 + 标签 + digest」三元组，再决定去哪拉。解析失败发生在**任何网络请求之前**——所以这条报错永远来自 CLI 本地校验，跟 daemon、镜像源、网络都没关系。它长得像网络问题，其实是纯字符串问题。",
          "",
          "确定性推论：报错时先把命令里最终拼出来的镜像名用 `echo` 打出来看，空格/大小写/未展开变量一眼可见；校验规则只有一条：仓库名全小写、无空格。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先看 docker 实际收到的镜像名，再用一个必然能过语法校验的命令对照",
          "echo \"$IMAGE\"",
          "docker run ubuntu echo hi   # 全小写、无空格、无引号残留",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 打印最终镜像名 — **必做**",
          "",
          "```bash",
          "# 报错的是哪条命令，就把它的镜像名部分 echo 出来",
          "echo \"ubuntu \"          # 你会看到尾部空格",
          "echo \"$TAG\"             # 变量是否为空 / 含非法字符",
          "```",
          "",
          "### 2. 按报错形态分流 — **按诊断结果**",
          "",
          "- 报错带 `repository name (library/Ubuntu) must be lowercase` → 仓库名有大写：改成全小写（`Ubuntu` → `ubuntu`）。",
          "- 报错只有 `invalid reference format` → 多半是空格或空字符串：去掉引号内多余空格，或变量先 `echo` 确认展开结果。",
          "- 变量场景：PowerShell 里 `${pwd}` 是「当前路径字符串」，不是 `$(pwd)` 的执行结果——用 `$(pwd)`（PowerShell）或 `${PWD}`（bash），或先展开成变量再拼。",
          "",
          "### 3. 用最简命令确认语法 — **必做**",
          "",
          "```bash",
          "docker run alpine echo ok   # 全小写 + 无空格，能跑通 = 你原命令的字符串有问题",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker run alpine echo ok` 输出 `ok` 且没有 `invalid reference format`；修正后的原命令能正常拉取/启动。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 仓库名大写 | `library/Ubuntu` 完整报错（实测） |",
          "| 值里带空格 | 短句 `docker: invalid reference format`（实测） |",
          "| digest 引用 | `image@sha256:...` 的 digest 格式不对同样报 invalid reference format |",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 形态 1：仓库名大写（完整文案）",
          "docker: invalid reference format: repository name (library/Ubuntu) must be lowercase",
          "",
          "# 形态 2：字符串里有空格（短句）",
          "docker: invalid reference format",
          "",
          "Run 'docker run --help' for more information",
          "```",
          "",
          "特征：`docker run` / `docker pull` 立即失败，exit 125，不经过网络；命令字符串里有大写/空格/未展开变量。",
          ""
        ],
        "原因": [
          "",
          "镜像引用有严格语法：仓库名只允许小写字母、数字和 `-_.`；标签允许 `[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}`。空格、大写仓库名、未展开变量都会让 CLI 本地解析失败——报错发生在任何网络请求之前，与 daemon/镜像源/网络无关。",
          ""
        ],
        "解法": [
          "",
          "1. `echo` 打印最终镜像名，检查空格/大小写/变量展开；",
          "2. 仓库名改全小写；去掉引号内多余空格；变量用 `$(pwd)`（PowerShell）或 `${PWD}`（bash）正确展开；",
          "3. `docker run alpine echo ok` 验证语法层通了，再回到原命令。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-31，WSL Ubuntu，docker CLI 29.1.3，本地 daemon 29.1.3）：",
          "  - 链 A：`docker run Ubuntu echo hi` → `docker: invalid reference format: repository name (library/Ubuntu) must be lowercase`（exit 125）；",
          "  - 链 B：`docker run \"ubuntu \" echo hi` → `docker: invalid reference format`（短句形态）；",
          "  - 对照：`docker run alpine echo ok` → 输出 `ok`，exit 0，无语法报错。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（先确认 daemon 活着，再查字符串；连接失败优先于引用解析）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-invalid-reference-format",
        "agent": "https://trove.xin/agent/problems/docker-invalid-reference-format"
      }
    },
    {
      "id": "docker-manifest-not-found",
      "title": "docker pull 报 manifest for \"xxx:latest\" not found（镜像或标签不存在）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "官方文档机制核对 + 本站 实测（2026-08-31，Docker 域试点；本机镜像源遮挡未复现标准形态）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "`docker pull some/image:latest`（或 run 一个不存在的镜像）报 `Error response from daemon: manifest for some/image:latest not found: manifest unknown`。",
          "",
          "镜像「不存在」有四种常见真相：**仓库名拼错**、**标签不存在**、**私有仓库没登录**、**镜像被作者删了/改了 tag**。另外注意：国内镜像加速器会把这条错误**改头换面**成 403（见下），认不出就白折腾。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`docker pull` 的链路是：CLI → registry 的 manifest API（`/v2/<仓库>/manifests/<标签>`）→ 拿到清单后再按层下载。**manifest 查询返回 404 = daemon 转成 `manifest for xxx not found`**。",
          "",
          "`latest` 不是特殊魔法值，只是一个默认标签：作者不推 `latest` 就只有具体版本标签；镜像被删/私有化同样 404。",
          "",
          "镜像 = manifest（清单，描述层与配置）+ 若干层 blob。pull 第一步只查 manifest，这一步的 404 直接对应「这个仓库+标签组合在 registry 上不存在」。daemon 的报错文案固定为 `manifest for <image>:<tag> not found: manifest unknown`（Docker Hub 官方形态）。",
          "",
          "国内加速器（daemon.json 的 registry-mirrors）是「代理 registry」：本机实测（daocloud 镜像源）对不存在的标签返回 **403 Forbidden** 而不是 404/manifest unknown——它把上游 404 伪装成自己的状态码，是镜像站自身行为，不是你的配置问题。",
          "",
          "确定性推论：报 `manifest unknown`/`not found` = registry 上确实没这组合，先查仓库名拼写、tag 是否真实存在（`docker manifest inspect` 或 registry 网页），私有仓库再考虑 `docker login`；",
          "",
          "报 `403 Forbidden from https://<镜像站>/v2/...` 且直连 Docker Hub 正常 = 镜像站行为，换镜像源或直接去上游仓库页面核对。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先用 manifest 查询确认远端存在性，再拉明确 tag（不赌 latest）",
          "docker manifest inspect alpine:3.20 && docker pull alpine:3.20",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认拼写与标签 — **必做**",
          "",
          "```bash",
          "docker manifest inspect <仓库>:<标签>   # 报 not found = 远端真没有",
          "docker search <仓库>                    # 确认仓库名拼写",
          "```",
          "",
          "### 2. 按场景分流 — **按场景**",
          "",
          "- 仓库名拼错 → 去 Docker Hub / 源仓库页面复制准确名称；",
          "- 只有版本标签没有 latest → 明确写版本号：`docker pull redis:7-alpine`；",
          "- 私有仓库 → `docker login <registry>` 后再拉；",
          "- 镜像被删/迁移（如从 Docker Hub 迁到 ghcr.io）→ 用新地址。",
          "",
          "### 3. 国内加速器 403 变体 — **按诊断结果**",
          "",
          "报 `unexpected status from HEAD request to https://<镜像站>/v2/... 403 Forbidden` 时：先 `docker info` 看当前 Registry Mirrors，直接访问上游仓库页面确认 tag 是否存在；",
          "",
          "镜像站把上游 404 改写成 403，换一个镜像源（改 `/etc/docker/daemon.json` 的 registry-mirrors）通常就能看到真实状态。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker manifest inspect <仓库>:<tag>` 返回 JSON 清单；`docker pull <仓库>:<tag>` 正常下载层。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Docker Hub 官方直连 | `manifest for ... not found: manifest unknown` |",
          "| 国内镜像加速器 | 可能变成 403 Forbidden（实测 daocloud） |",
          "| 私有 registry | 未登录常报 `pull access denied`，登录后正常 |",
          "| 多架构镜像 | 缺某架构报 `no matching manifest for linux/arm64`，按架构换 `--platform` |",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 标准形态（Docker Hub 直连）",
          "Error response from daemon: manifest for some/image:latest not found: manifest unknown",
          "",
          "# 本机实测形态（daocloud 镜像源，2026-08-31）",
          "Error response from daemon: unknown: failed to resolve reference \"docker.io/library/alpine:definitely-no-tag-xyz\":",
          "unexpected status from HEAD request to https://docker.m.daocloud.io/v2/library/alpine/manifests/definitely-no-tag-xyz?ns=docker.io: 403 Forbidden",
          "```",
          "",
          "特征：报错里带「仓库:标签」完整引用；`manifest ... not found` = registry 上没这组合；镜像站域名 + 403 = 加速器把上游 404 改写了。",
          ""
        ],
        "原因": [
          "",
          "pull 第一步查 manifest API，404 → daemon 报 `manifest not found`。`latest` 只是默认标签不是保证；镜像被删/改名/私有化/拼错都表现为 not found。镜像加速器是代理层，会把上游 404 改写成自己的状态码（实测 403），识别特征 = 报错 URL 是镜像站域名。",
          ""
        ],
        "解法": [
          "",
          "1. `docker manifest inspect` 确认远端存在性；",
          "2. 拼错改拼写、无 latest 写版本号、私有仓库先 `docker login`、被删/迁移换地址；",
          "3. 镜像站 403 变体 = 换镜像源或直连上游验证。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-31，WSL Ubuntu，docker 29.1.3，daemon.json 配置 1panel.live + daocloud 镜像源）：`docker pull alpine:definitely-no-tag-xyz` → 镜像站 403 Forbidden（上游 404 被改写）；`docker pull ubuntu:1.0_` → 同镜像站 403。标准形态 `manifest for ... not found: manifest unknown` 未在本机复现（镜像源遮挡），机制与文案以 Docker 官方文档/多源搜索核对为准，链路推理由 manifest API 语义给出。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **同根（同一根因）**：",
          "  - [Docker Hub 拉取失败](docker-hub-pull-fail.html)（网络层失败：connection reset / no such host——与「镜像不存在」同症状族、不同根因）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-manifest-not-found",
        "agent": "https://trove.xin/agent/problems/docker-manifest-not-found"
      }
    },
    {
      "id": "docker-port-already-allocated",
      "title": "docker run / compose up 报 port is already allocated / address already in use（端口被占用）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/lobehub/lobehub/issues/5700",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`docker run -p <宿主端口>:<容器端口>` 或 `docker compose up` 时容器创建失败，报 `failed to bind host port 0.0.0.0:8080/tcp: address already in use` 或 `Bind for :::8090 failed: port is already allocated`。",
          "",
          "同一个 `-p` 命令之前能跑通，某天突然报错——**宿主端口被占了**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`-p 宿主端口:容器端口` 会在宿主机上创建端口转发监听（docker-proxy 监听 `0.0.0.0:宿主端口`）。宿主端口同一时间只能被一个进程/容器占用。两大类占用来源：",
          "",
          "| 占用来源 | 例子 |",
          "|---|---|",
          "| 宿主机其它进程 | python http.server、nginx、VSCode 端口转发 |",
          "| 其它 docker 容器 | 另一个容器映射同一宿主端口（compose 两个服务撞端口、旧容器没删干净） |",
          "",
          "`-p 宿主端口:容器端口` 会在宿主机创建一个转发监听（docker-proxy 监听 `0.0.0.0:宿主端口`），语义是「宿主端口 → 容器端口」的映射。宿主端口是宿主机全局资源，同一时间只能有一个监听者——不管占用者是宿主进程还是另一个容器的转发规则。",
          "",
          "确定性推论：报 `port is already allocated` 先 `ss -lntp | grep <端口>` 看监听者是谁；监听进程是 docker-proxy = 另一个容器占着，`docker ps -a` 找旧容器——**已退出的容器保留端口映射**，`docker ps` 看不到不代表端口空闲；监听者是宿主进程 = 换端口或停进程。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先看是不是 docker 容器占的，再看宿主机进程，最后换端口",
          "docker ps | grep <端口> ; ss -tlnp | grep <端口>",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看是不是 docker 容器占的 — **必做**",
          "",
          "```bash",
          "docker ps",
          "```",
          "",
          "看 Ports 列有没有该端口。有就 `docker rm -f <旧容器>`（或 stop + rm），确认没有别的服务需要它再删。",
          "",
          "### 2. 再看宿主机进程 — **必做**",
          "",
          "```bash",
          "ss -tlnp | grep <端口>",
          "```",
          "",
          "（或 `lsof -i:<端口>` / `netstat -tulpn | grep <端口>`）看到 PID/进程名就能定位是谁占的——是临时进程就停掉，是长期服务就换端口。",
          "",
          "### 3. 换宿主端口（最省事） — **必做**",
          "",
          "```bash",
          "docker run -p 8081:80 <镜像>",
          "```",
          "",
          "compose 里改 `ports:` 左侧的宿主端口（如 `\"8081:80\"`），或写成环境变量统一管理（`${LOBE_PORT}:3210` 这类）。",
          "",
          "### 4. 排查 compose 内部冲突 — **排查**",
          "",
          "同一个 compose 文件里两个服务映射同一宿主端口会互相打架，逐个检查 `ports:` 左侧值是否唯一。"
        ],
        "怎么确认修好了": [
          "",
          "换端口后正常（实测输出形态）：",
          "",
          "```text",
          "docker run -p 8081:80 → 成功",
          "docker port <容器> → 80/tcp -> 0.0.0.0:8081",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 宿主进程占用 | `ss -tlnp` 定位 PID，停进程或换端口（实测链 A） |",
          "| 容器占用 | `docker ps` Ports 列定位，rm -f 旧容器（实测链 B） |",
          "| compose 内部 | ports 左侧值唯一 |",
          "| IPv6 侧 | `Bind for :::8090 failed` = 容器占用形态（实测） |",
          ""
        ],
        "症状": [
          "",
          "`docker run -p <宿主端口>:<容器端口>` 或 `docker compose up` 时容器创建失败，两种典型报错：",
          "",
          "```",
          "# 形态 1：宿主上其它进程占了端口",
          "docker: Error response from daemon: failed to set up container networking:",
          "driver failed programming external connectivity on endpoint <name>:",
          "failed to bind host port 0.0.0.0:8080/tcp: address already in use",
          "",
          "# 形态 2：另一个 docker 容器占了端口（常报 IPv6 侧）",
          "docker: Error response from daemon: failed to set up container networking:",
          "driver failed programming external connectivity on endpoint <name>:",
          "Bind for :::8090 failed: port is already allocated",
          "```",
          "",
          "特征：报错里一定带 `port is already allocated` 或 `address already in use`，且明确指向宿主端口（0.0.0.0:PORT / :::PORT）；同一个 `-p` 命令**之前能跑通**，某天突然报错；或照教程/别人的 compose 起服务时报错（端口撞车）。",
          ""
        ],
        "原因": [
          "",
          "`-p 宿主端口:容器端口` 会在宿主机上创建一个端口转发监听（docker-proxy 监听 `0.0.0.0:宿主端口`）。宿主端口同一时间只能被一个进程/容器占用，被占就报上面的错。两大类占用来源：",
          "",
          "1. **宿主机其它进程**：你/系统/别的服务（如 python http.server、nginx、VSCode 端口转发）占着该端口——最常见；",
          "2. **其它 docker 容器**：另一个容器的 `-p` 映射了同一宿主端口（含 compose 里两个服务映射同一端口、或旧容器没删干净）。",
          ""
        ],
        "解法": [
          "",
          "1. **先看是不是 docker 容器占的**：`docker ps`（看 Ports 列有没有该端口）。有就 `docker rm -f <旧容器>`（或 `docker stop` + `docker rm`），确认没有别的服务需要它再删。",
          "2. **再看宿主机进程**：`ss -tlnp | grep <端口>`（或 `lsof -i:<端口>` / `netstat -tulpn | grep <端口>`），看到 PID/进程名就能定位是谁占的——是临时进程就停掉，是长期服务就换端口。",
          "3. **换宿主端口（最省事）**：`-p 8081:80` 换成没被占的端口；compose 里改 `ports:` 左侧的宿主端口（如 `\"8081:80\"`），或把端口写成环境变量统一管理（`${LOBE_PORT}:3210` 这类）。",
          "4. **排查 compose 内部冲突**：同一个 compose 文件里两个服务映射同一宿主端口会互相打架，逐个服务检查 `ports:` 左侧值是否唯一。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker 29.1.3，alpine:3.20）：",
          "  - 链 A（宿主进程占用）：python3 http.server 占 8080 → `docker run -p 8080:80` → `failed to bind host port 0.0.0.0:8080/tcp: address already in use`（exit 125）；`ss -tlnp` 定位到 python3 进程（pid 可见）；停掉后换 `-p 8081:80` → 成功，`docker port` 显示 `80/tcp -> 0.0.0.0:8081`，docker-proxy 监听 0.0.0.0:8081 与 [::]:8081；",
          "  - 链 B（容器占用）：容器 1 `-p 8090:80` 成功 → 容器 2 抢同一端口 → `Bind for :::8090 failed: port is already allocated`（exit 125）；`docker ps` Ports 列定位到旧容器；`docker rm -f` 旧容器后同一端口再起成功。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：先确认 daemon 活着，端口报错才有意义）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-port-already-allocated",
        "agent": "https://trove.xin/agent/problems/docker-port-already-allocated"
      }
    },
    {
      "id": "docker-prune-scope",
      "title": "docker system prune 到底删什么？容器/镜像/卷/缓存清理边界速查",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "本机实测复现（2026-08-31，Docker 域缺口 #19）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "磁盘吃紧想清理 Docker：`docker system prune -f` 跑完发现空间没怎么回来；`docker volume prune` 显示 `Total reclaimed space: 0B`；或者反方向——照着「清理教程」执行后，**正在用的镜像/卷被删了，服务起不来**。",
          "",
          "prune 系列命令的删除范围各不相同，默认值还有反直觉的坑（比如 `docker volume prune` 默认**只删未用的匿名卷**，named 卷要 `-a` 才删——实测确认）。",
          "",
          "> 本文路径：**速查表**（一条命令一列）→ **安全清理套路** → **实测演示与原理**。赶时间直接看「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "prune 家族是「按类别回收」命令，每个命令有自己的默认范围，`-a`/`--volumes`/`--filter` 是范围开关：",
          "",
          "| 命令 | 删除范围 | 备注 |",
          "|---|---|---|",
          "| `docker system prune` | 停止的容器 + dangling 镜像 + 未用网络 | 不碰卷、不碰带 tag 的未用镜像、不碰 build cache |",
          "| `docker system prune -a` | + 所有未用镜像（含带 tag） | 危险：会删掉缓存着但没在用的镜像 |",
          "| `docker system prune --volumes` | + 未用**匿名**卷 | 不删 named 卷（help 原文：Prune anonymous volumes） |",
          "| `docker volume prune` | 未用**匿名**卷 | **默认不删 named 卷**（本版本实测，help 原文见下） |",
          "| `docker volume prune -a` | 所有未用卷（含 named） | 带 `--filter label=` 可精确圈定 |",
          "| `docker container prune` | 停止的容器 | `--filter label=` / `until=` |",
          "| `docker image prune` | dangling 镜像 | `-a` 才含未用带 tag 镜像 |",
          "| `docker network prune` | 未用网络 | `--filter label=` |",
          "| `docker builder prune` | 构建缓存 | 可安全回收（只影响下次构建速度） |",
          "",
          "三个默认值最容易踩坑，全部实测：① `system prune` 默认删**停止的容器**——你以为只是清缓存，结果所有 `docker create`/exit 的容器都没了（包括你准备 `docker start` 复用的）；",
          "",
          "② 卷是「默认不碰」：`system prune` 不带 `--volumes` 一个卷都不删，而 `volume prune` 默认又**只删匿名卷**（`--help` 原文 `-a, --all  Remove all unused volumes, not just anonymous ones`，实测 named 卷在无 `-a` 时原样保留）——两个默认方向相反，混着用必踩；",
          "",
          "③ dangling 镜像 = 被新构建顶掉、没有 tag 的旧镜像（`<none>:<none>`），`image prune` 默认只清这类，**带 tag 的未用镜像要 `-a` 才删**（实测：`system prune` 后 `prune-demo-img:latest` 原样存在）。",
          "",
          "确定性推论：删之前先 `docker system df` 看各类占比；共享环境用 `--filter label=你的标识` 圈定自己的产物，绝不在共享 daemon 上跑无过滤的 `system prune -a`。",
          ""
        ],
        "快速修复（安全清理套路）": [
          "",
          "```bash",
          "# 1. 先看占比，别盲删",
          "docker system df",
          "",
          "# 2. 只清确定不要的：停止容器 + dangling 镜像 + 未用网络",
          "docker system prune -f",
          "",
          "# 3. 卷是另一个维度，确认后单独清（-a 才会动 named 卷）",
          "docker volume ls -f dangling=true          # 先列出“未用”卷，确认没有要留的",
          "docker volume prune -a -f                  # 或带 label 圈定：--filter label=my-project",
          "",
          "# 4. 构建缓存随时可清",
          "docker builder prune -f",
          "```",
          "",
          "```bash",
          "# 共享 daemon 上的隔离姿势：所有产物带 label，清理用 filter 圈定",
          "docker run --label my-project=true ...",
          "docker volume create --label my-project=true ...",
          "docker system prune -f --filter label=my-project=true",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看空间去哪了 — **必做**",
          "",
          "```bash",
          "docker system df",
          "# Images / Containers / Local Volumes / Build Cache 四类占比，先看再删",
          "```",
          "",
          "### 2. 删容器 — **按诊断**",
          "",
          "停止的容器用 `docker container prune -f`；想精确圈自己的：`docker container prune -f --filter label=my-project=true`（实测：只删匹配容器）。",
          "",
          "### 3. 删镜像 — **按诊断**",
          "",
          "`docker image prune -f` 只清 dangling；`-a` 会把未用的带 tag 镜像也删（实测：`system prune` 默认删了 dangling 但 `prune-demo-img:latest` 保留；`docker image rm <tag>` 才删带 tag 的）。共享环境别用无过滤 `-a`。",
          "",
          "### 4. 删卷 — **必做（最容易误删/漏删）**",
          "",
          "```bash",
          "docker volume ls -f dangling=true      # 未用卷清单，先人工确认",
          "docker volume prune -a -f              # 默认只删匿名卷！-a 才含 named",
          "```",
          "",
          "实测：`docker volume prune -f` 只删了匿名卷，`prune-demo-named` 原样保留；`docker volume prune -a -f --filter label=prune-demo=true` 才删掉它。",
          "",
          "### 5. 删构建缓存 — **可做**",
          "",
          "`docker builder prune -f`：只影响下次构建速度，不影响镜像/容器。",
          ""
        ],
        "怎么确认清理安全": [
          "",
          "删完后 `docker ps -a`、`docker images`、`docker volume ls` 里你需要的都在；`docker compose up` 能正常拉起（卷数据、带 tag 镜像都还在）。实测演示：`system prune`（默认）后 tagged 镜像 + named 卷均保留，只有停止容器/dangling/未用网络被删。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 命令/参数 | 实测行为（docker 29.1.3） |",
          "|---|---|",
          "| `system prune`（默认） | 删停止容器 + dangling 镜像 + 未用网络；卷/tagged 镜像/build cache 保留 |",
          "| `system prune -a` | 加删所有未用镜像（含带 tag） |",
          "| `system prune --volumes` | 加删未用匿名卷（help 原文） |",
          "| `volume prune`（默认） | 只删未用匿名卷（`-a` 原文：not just anonymous ones） |",
          "| `volume prune -a` | 删所有未用卷（含 named） |",
          "| `--filter label=` | 容器/卷/网络/镜像均支持，精确圈定（实测） |",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 清理后空间没回来（卷没删）",
          "$ docker volume prune -f",
          "Total reclaimed space: 0B        # named 卷默认不删，这就是原因",
          "",
          "# 清理教程翻车：正在用的镜像被删",
          "$ docker system prune -a -f      # 未用带 tag 镜像全没了 → 下次启动要重新拉",
          "```",
          "",
          "特征：prune 后空间没变化（卷/带 tag 镜像没被默认覆盖）；或反噬（无过滤 `-a` 删了共享镜像）。",
          ""
        ],
        "原因": [
          "",
          "prune 家族各命令默认范围不同：`system prune` 默认含停止容器与 dangling 镜像但不含卷；`volume prune` 默认只删未用匿名卷；带 tag 的未用镜像需 `-a`。共享环境跑无过滤 `-a`/`--volumes` 会误删。",
          ""
        ],
        "解法": [
          "",
          "1. `docker system df` 先看四类占比；",
          "2. 按目标选命令：容器=`container prune`、dangling 镜像=`image prune`、卷=`volume prune -a`（确认后）、缓存=`builder prune`；",
          "3. 共享环境所有产物打 `--label`，清理用 `--filter label=` 圈定；",
          "4. 删完用 `docker ps -a` / `docker images` / `docker volume ls` 复核。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测复现（2026-08-31，WSL 定制预构建版，docker 29.1.3；全部演示产物带 `label=prune-demo=true`，用 `--filter label=` 隔离，未运行无过滤 system prune）：",
          "  - 构造：两次 build 制造 dangling 镜像 + 2 个停止容器（1 个带匿名卷）+ named 卷 + 网络；",
          "  - `system prune -f --filter label=prune-demo=true`：删 2 容器 + 1 网络 + 1 dangling 镜像（untagged: e71931f35c62），tagged `prune-demo-img:latest` 与 `prune-demo-named` 卷保留；",
          "  - `volume prune -f`：只删匿名卷（25b060c64c02…），named 卷保留；",
          "  - `volume prune -a -f --filter label=prune-demo=true`：删 named 卷；",
          "  - `volume prune --help` 原文 `-a, --all  Remove all unused volumes, not just anonymous ones`；",
          "  - `builder prune -f`：本机 build cache 为空，0B（语义验证）。",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [WSL 报 No space left 但 df 还有空间](wsl-no-space-left.html)（同为「空间没了」排查：WSL 虚盘 vs Docker 占用）",
          "  - [docker compose 起栈失败：依赖没就绪](docker-compose-depends-not-ready.html)（同批 Docker 域实测 canonical）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-prune-scope",
        "agent": "https://trove.xin/agent/problems/docker-prune-scope"
      }
    },
    {
      "id": "docker-volume-permission",
      "title": "Docker bind mount / volume 权限报错（Permission denied）：容器进程无法读写挂载目录",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/0xERR0R/blocky/issues/2165",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "容器能正常启动、挂载也能看到（ls 有文件），但进程一读一写就报 `Permission denied`。你以为是挂载没成功——其实挂载成功了，是**容器内进程的 uid 和宿主目录的属主对不上**。常见于按安全建议切非 root 用户跑容器（`-u 1000:1000`、compose `user:`、镜像内置 USER）之后；镜像默认 root 跑时一切正常，一切换立刻暴露。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Linux 权限按 uid/gid 匹配，**bind mount 不做所有权转换**：容器进程的 uid 直接按宿主目录文件的属主/权限来判定。典型错位：宿主文件属主 root（uid 0），容器进程以非 0 uid 运行 → 读 600 被拒、写不进。",
          "",
          "**常见误区**：换 named volume 也不自动解决——docker 首次创建 named volume 时把目录初始化为 `root:root 755`，非 root 容器照样 Permission denied（实测复现）。",
          "",
          "> 实测环境：WSL Ubuntu，docker 29.1.3，alpine:3.20，2026-08-29。",
          "",
          "Linux 权限判定是「进程 uid/gid × 文件属主/权限位」的匹配，bind mount 只是把宿主目录挂进容器，**不做 uid 映射也不做所有权转换**——容器里 uid 1000 的进程读写宿主上 uid 0 的 600 文件，判定结果与宿主机上一个普通用户读写 root 文件完全一致。",
          "",
          "named volume 首次创建时 docker 初始化为 `root:root 755`，所以非 root 容器换 named volume 照样 Permission denied。",
          "",
          "确定性推论：别猜「容器里应该能写」，先 `docker run --rm -v <目录>:/data <镜像> ls -ln /data` 看实际属主；解法三选一——宿主侧 `chown -R 1000:1000` 对齐 uid、容器以 root 跑（临时/开发）、user namespace 重映射（生产）；chown 改的是宿主文件，容器内外两个方向同时生效。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：把宿主目录属主改成容器内 uid（示例容器 uid 1000）",
          "sudo chown -R 1000:1000 ./data && docker run --rm -u 1000:1000 -v \"$PWD/data:/data\" <镜像> ls /data",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是权限不是挂载 — **必做**",
          "",
          "```bash",
          "docker run --rm -u 0:0 -v \"$PWD/data:/data\" <镜像> ls -l /data",
          "```",
          "",
          "root 容器能读、非 root 容器不能 → 权限问题（实测对照）。",
          "",
          "### 2. 调整宿主目录属主 — **必做**",
          "",
          "```bash",
          "sudo chown -R 1000:1000 ./data",
          "```",
          "",
          "chown 成容器内 uid——一次性修正，适合数据目录属主固定不变的场景（实测：chown 后非 root 容器读取成功，exit 0）。",
          "",
          "### 3. 镜像内置用户时查官方文档 — **必做**",
          "",
          "很多官方镜像写明「请用 `-u 1000:1000` 运行」（如 blocky 内部用户是 uid 100），按文档对齐，不要猜。",
          "",
          "### 4. named volume 的权限初始化 — **必做**",
          "",
          "若镜像内目标目录属主不匹配，需要在 entrypoint / init 容器里 chown；镜像目录属主本来正确（如镜像里建好 `/data` 且属主 1000）时才能直接享受 volume 自动初始化。",
          "",
          "### 5. 临时应急 — **必做**",
          "",
          "`-u 0:0` 以 root 跑容器可绕过，但破坏最小权限原则，仅限本机调试。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "docker run --rm -u 1000:1000 -v \"$PWD/data:/data\" <镜像> touch /data/write-test && echo OK",
          "```",
          "",
          "能写入即成功。",
          "",
          "实测：`-u 1000:1000` 读 root 属主 600 文件 → Permission denied（exit 1）；root 容器读同一挂载 → 成功；宿主 `chown 1000:1000` 后非 root 容器 → 成功；named volume 初始化属主 root:root 755，非 root 容器 touch → 仍 Permission denied（误区坐实）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| bind mount（-v 宿主机:容器） | 最典型，chown 宿主目录对齐 uid |",
          "| named volume | 不自动解决；镜像目录属主正确才行 |",
          "| Docker Desktop（Windows/macOS） | 同样高频，挂载目录属主与容器 uid 对不上 |",
          "| 镜像内置 USER | 查官方文档的推荐 `-u`，按文档对齐 |",
          ""
        ],
        "症状": [
          "",
          "容器能正常启动、挂载也能看到（ls 有文件），但进程一读一写就报权限错误：",
          "",
          "```",
          "cat: can't open '/data/rootfile': Permission denied",
          "touch: /data/write-test: Permission denied",
          "mkdir: cannot create directory '/data/xxx': Permission denied",
          "cannot create temp cache file in /cache: open /cache/xxx.tmp: permission denied",
          "```",
          "",
          "特征：",
          "",
          "- 容器起得来、目录可见，只是读写被拒——容易误判成「挂载没成功」；",
          "- 常见于非 root 容器：镜像内置 `USER`、`docker run -u UID:GID`、compose 的 `user: \"1000:1000\"`；",
          "- bind mount（`-v /宿主机路径:/容器路径`）最典型；named volume 也会出现；",
          "- Windows/macOS 的 Docker Desktop 同样高频（挂载自宿主目录，属主与容器内 uid 对不上）。",
          ""
        ],
        "原因": [
          "",
          "Linux 权限按 uid/gid 匹配，**bind mount 不做所有权转换**：容器进程的 uid 直接按宿主目录文件的属主/权限来判定。典型错位是宿主文件属主 root（uid 0）或别的 uid（如 1000），而容器进程以非 0 uid 运行 → 读 600 / 写不进去。镜像默认以 root 跑时一切正常，一旦按安全建议切非 root 用户跑，问题立刻暴露。",
          "",
          "**常见误区**：换 named volume 也不自动解决——docker 首次创建 named volume 时把目录初始化为 root:root 755，非 root 容器照样 Permission denied（实测复现）。",
          ""
        ],
        "解法": [
          "",
          "1. **调整宿主目录属主**：`sudo chown -R 1000:1000 ./data`（chown 成容器内 uid）——一次性修正，适合数据目录属主固定不变的场景。",
          "2. **镜像内置用户时查官方文档**：很多官方镜像写明「请用 `-u 1000:1000` 运行」（如 blocky 内部用户是 uid 100），按文档对齐，不要猜。",
          "3. **named volume 的权限初始化**：若镜像内目标目录属主不匹配，需要在 entrypoint / init 容器里 chown；镜像目录属主本来正确（如镜像里建好 `/data` 且属主 1000）时才能直接享受 volume 自动初始化。",
          "4. **临时应急**：`-u 0:0` 以 root 跑容器可绕过，但破坏最小权限原则，仅限本机调试。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，docker 29.1.3，alpine:3.20）：",
          "  - 准备：宿主目录放 root 所有（uid 0）、600 权限的文件；",
          "  - 复现：`docker run --rm -u 1000:1000 -v /tmp/vol-test:/data alpine cat /data/rootfile` → `Permission denied`（exit 1）；",
          "  - 对照：不加 `-u` 的 root 容器读同一挂载 → 成功（排除挂载本身问题）；",
          "  - 修复 A：宿主 `chown 1000:1000` 后非 root 容器读取 → 成功（exit 0）；",
          "  - 修复 B：`-u 0:0` 对齐宿主 → 成功（exit 0）；",
          "  - 误区验证：`docker volume create` 后 `-u 1000:1000` 容器 touch named volume → `Permission denied`（exit 1）；`ls -ld /data` 证实 volume 初始化属主 `root:root 755` → named volume 不自动解决权限；root 容器写同一 named volume → 成功。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（前置：能启动容器 = daemon 正常，权限错在挂载层）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/docker-volume-permission",
        "agent": "https://trove.xin/agent/problems/docker-volume-permission"
      }
    },
    {
      "id": "execpolicy-restricted",
      "title": "powershell 运行 .ps1 报「禁止运行脚本」——执行策略（ExecutionPolicy）限制",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "powershell"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_execution_policies",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "运行 PowerShell 脚本直接失败：`File C:\\path\\setup.ps1 cannot be loaded because running scripts is disabled on this system.`（中文系统：无法加载文件...因为在此系统上禁止运行脚本）。交互式命令（`Get-Process` 等）正常，只有「执行脚本文件」被拦。",
          "",
          "常见触发：Windows 客户端默认 `Restricted`、企业组策略锁定、或下载的脚本带 MOTW 标记被 `RemoteSigned` 拦。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 要点 | 说明 |",
          "|---|---|",
          "| 执行策略分层 | MachinePolicy → UserPolicy → Process → CurrentUser → LocalMachine，前面的层优先级更高 |",
          "| `-File` 不绕过策略 | `powershell -File xxx.ps1` 依然受执行策略约束——很多人以为带 -File 就能跑 |",
          "| RemoteSigned 语义 | 本地未签名脚本可跑；**从互联网下载**的脚本带 Zone.Identifier（MOTW）标记，无签名被拦 |",
          "| 改动不生效的坑 | `Set-ExecutionPolicy RemoteSigned` 默认只改 CurrentUser；企业策略非 Undefined 时用户层改了也没用 |",
          "",
          "PowerShell 执行策略是**分层叠加**：MachinePolicy → UserPolicy → Process → CurrentUser → LocalMachine，前面层优先。",
          "",
          "`powershell -File xxx.ps1` 不绕过策略（很多人以为带 -File 就能跑）；RemoteSigned 下「从互联网下载」的脚本带 Zone.Identifier（MOTW 标记），无签名被拦——本地创建的脚本不受影响。",
          "",
          "两个「改了不生效」的坑：`Set-ExecutionPolicy RemoteSigned` 默认只改 CurrentUser（LocalMachine 需要管理员且策略非 Undefined 时用户层无效）；企业策略（MachinePolicy/UserPolicy）非 Undefined 时，用户层设置全部被压过。",
          "",
          "确定性推论：`Get-ExecutionPolicy -List` 看完整分层，找到哪层是实际生效值；「下载的脚本被拦」 = 解除 MOTW（`Unblock-File`）而不是关策略；改不生效 = 看是不是被更高层策略压住；判断路径：先查 -List 再动手。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：进程级临时绕过（最推荐，不写注册表）",
          "powershell -NoProfile -ExecutionPolicy Bypass -File .\\setup.ps1",
          "```",
          "",
          "只对当前进程生效，不改任何持久设置；`-NoProfile` 避免用户 profile 干扰。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 进程级临时绕过 — **必做**",
          "",
          "```bash",
          "powershell -NoProfile -ExecutionPolicy Bypass -File .\\setup.ps1",
          "```",
          "",
          "### 2. 当前会话级 — **必做**",
          "",
          "```powershell",
          "Set-ExecutionPolicy -Scope Process RemoteSigned",
          ".\\setup.ps1",
          "```",
          "",
          "关掉窗口即恢复。",
          "",
          "### 3. 用户级持久 — **必做**",
          "",
          "```powershell",
          "Set-ExecutionPolicy -Scope CurrentUser RemoteSigned",
          "Get-ExecutionPolicy -List",
          "```",
          "",
          "影响当前用户全部会话；确认 CurrentUser 行已变化。",
          "",
          "### 4. 企业/组策略锁定 — **必做**",
          "",
          "MachinePolicy 非 Undefined 时用户级命令无效，需要管理员在组策略里放开，或申请例外；不要在本地硬改注册表对抗策略。",
          "",
          "### 5. 下载的脚本被 RemoteSigned 拦 — **必做**",
          "",
          "```powershell",
          "Unblock-File .\\downloaded.ps1",
          "```",
          "",
          "去掉 MOTW 标记（仅当信任来源）。"
        ],
        "怎么确认修好了": [
          "",
          "脚本正常执行（实测输出形态）：",
          "",
          "```text",
          "powershell -ExecutionPolicy Bypass -File payload.ps1 → 正常输出，exit=0",
          "```",
          "",
          "检查当前策略与分层：`Get-ExecutionPolicy -List`；查 MOTW：`Get-Content -Stream Zone.Identifier .\\x.ps1`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Restricted | 禁止任何脚本文件（含本地文件） |",
          "| RemoteSigned | 本地可跑；下载脚本需 Unblock-File 或签名 |",
          "| 企业策略 | 用户级命令无效，走组策略 |",
          "| -File 参数 | 不绕过策略（实测受限时同样报错） |",
          "| 未完整复现项 | 手动写 Zone.Identifier 后本机未拦截（MOTW 缓存/环境差异），机制以官方文档为准 |",
          ""
        ],
        "症状": [
          "",
          "运行 PowerShell 脚本直接失败，报错类似：",
          "",
          "```",
          "powershell -File .\\setup.ps1",
          "File C:\\path\\setup.ps1 cannot be loaded because running scripts is disabled on this system.",
          "（中文系统：无法加载文件 ...，因为在此系统上禁止运行脚本。）",
          "    + CategoryInfo          : SecurityError: (:) [], ParentContainsErrorRecordException",
          "    + FullyQualifiedErrorId : UnauthorizedAccess",
          "```",
          "",
          "- 在 PowerShell 提示符里直接 `.\\script.ps1` 或 `& .\\script.ps1` 也可能报同一错误。",
          "- 交互式命令（`Get-Process`、`Set-Location` 等）正常，只有「执行脚本文件」被拦。",
          "- 常见触发：Windows 客户端默认 `Restricted`；或企业组策略把 MachinePolicy 锁成 `Restricted`/`AllSigned`；或从网上下载的脚本带「来自 Internet」标记（Zone.Identifier）在 `RemoteSigned` 下无签名被拦。",
          ""
        ],
        "原因": [
          "",
          "1. **执行策略分层**：`MachinePolicy` → `UserPolicy` → `Process` → `CurrentUser` → `LocalMachine`，**前面的层优先级更高**，`Get-ExecutionPolicy -List` 可见全部分层；`Restricted` = 禁止任何脚本文件（含本地文件）。",
          "2. **`-File` 参数不绕过策略**：`powershell -File xxx.ps1` 依然受执行策略约束——很多人以为带 `-File` 就能跑，实际不行。",
          "3. **RemoteSigned 语义**：本地创建的未签名脚本可以跑；但**从互联网下载**的脚本带 Zone.Identifier（MOTW）标记，无数字签名时被拦——这是「下载的脚本跑不了」的常见原因。",
          "4. **改动不生效的常见坑**：`Set-ExecutionPolicy RemoteSigned` 默认只改 `CurrentUser`，若企业策略（MachinePolicy/UserPolicy）非 Undefined，改用户层不生效；或改了 LocalMachine 但无管理员权限报错。",
          ""
        ],
        "解法": [
          "",
          "按「不改系统 → 会话级 → 用户级 → 企业级」顺序选：",
          "",
          "1. **进程级临时绕过（最推荐，不写注册表）**：",
          "   ```",
          "   powershell -NoProfile -ExecutionPolicy Bypass -File .\\setup.ps1",
          "   ```",
          "   只对当前进程生效，不改任何持久设置；`-NoProfile` 避免用户 profile 干扰。",
          "",
          "2. **当前会话级**（关掉窗口即恢复）：",
          "   ```",
          "   Set-ExecutionPolicy -Scope Process RemoteSigned",
          "   .\\setup.ps1",
          "   ```",
          "",
          "3. **用户级持久**（影响当前用户全部会话）：",
          "   ```",
          "   Set-ExecutionPolicy -Scope CurrentUser RemoteSigned",
          "   Get-ExecutionPolicy -List   # 确认 CurrentUser 行已变化",
          "   ```",
          "",
          "4. **企业/组策略锁定（MachinePolicy 非 Undefined）时**：用户级命令无效，需要管理员在组策略里放开，或找管理员申请例外；不要在本地硬改注册表对抗策略。",
          "",
          "5. **下载的脚本被 RemoteSigned 拦**（报错带 `UnauthorizedAccess` + 文件来源是浏览器下载）：用 `Unblock-File` 去掉 MOTW 标记（仅当信任来源）：",
          "   ```",
          "   Unblock-File .\\downloaded.ps1",
          "   ```",
          "",
          "检查当前策略与分层：`Get-ExecutionPolicy -List`；查看某文件是否带 MOTW：`Get-Item .\\x.ps1 | Select-Object -ExpandProperty VersionInfo` 或 `Get-Content -Stream Zone.Identifier .\\x.ps1`。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机基线（Windows PowerShell 5.1，2026-08-29）：`CurrentUser = RemoteSigned`。",
          "- 实测 1（症状复现）：进程级 `Restricted` + `-File payload.ps1` → 报 `running scripts is disabled on this system`（SecurityError/UnauthorizedAccess），exit=1——与用户报错逐字一致。",
          "- 实测 2（修复 1）：`-ExecutionPolicy Bypass -File` → 脚本正常输出，exit=0。",
          "- 实测 3（RemoteSigned 语义）：本地未签名脚本在 `RemoteSigned` 下可运行，exit=0。",
          "- 实测 4（如实标注）：手动写入 Zone.Identifier（ZoneId=3）模拟远程下载后，本机 `RemoteSigned` 未拦截（MOTW 缓存/环境差异）——「下载脚本被拦」场景未在本环境完整复现，机制与修复命令以官方文档为准。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/execpolicy-restricted",
        "agent": "https://trove.xin/agent/problems/execpolicy-restricted"
      }
    },
    {
      "id": "float-crash",
      "title": "程序偶发 SIGSEGV 崩溃，gdb 显示 best=DBL_MAX 且指针为空——浮点比较\"恒不成立\"时哨兵从未被覆盖",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "dev-debug",
      "category_label": "编程与调试",
      "applies_to": [
        "gcc"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/KBin/p/22614217",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "真实案例：MariaDB 5.5.64 在国产 ARM64 服务器（信创早期、CPU 无 FPU）上偶发崩溃，SIGSEGV s=11；gdb 现场 `best_key = 0x0`（空指针）+ `best = DBL_MAX`（1.797e+308 哨兵初值，从未被任何候选覆盖）。看起来\"偶发\"，因为只有数据/环境触发到比较恒 false 时才发生，平时一切正常。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "任何「用 `DBL_MAX` 做最优初值 + 指针记录最优项」的搜索/选择代码，一旦所有比较都不成立，哨兵不被覆盖 → 指针保持 NULL → 解引用即崩溃。三层原因：",
          "",
          "| 层 | 机制 |",
          "|---|---|",
          "| 环境根因 | 信创早期 ARM64 CPU 无 FPU → 系统按软浮点 ABI 编译；MariaDB 二进制按硬浮点 ABI——浮点寄存器约定错位 → 值被读成 NaN/错误值 |",
          "| 机制根因 | IEEE 754 下 **NaN 参与任何比较恒为 false**（`NaN < x` 不成立）→ `if (score < best)` 恒 false → best 保持 DBL_MAX、best_key 保持 NULL |",
          "| 代码缺陷 | 使用前未判空/未校验——`best_key` 就是\"有无候选命中\"的有效性哨兵，但代码直接解引用 |",
          "",
          "崩溃的传导链是「ABI 错位 → NaN → 比较恒 false → 哨兵未覆盖 → 空指针解引用」：环境层，无 FPU 的 ARM64 按软浮点 ABI 编译系统组件，MariaDB 二进制按硬浮点 ABI，浮点寄存器约定错位，值被读成 NaN/错误值；",
          "",
          "机制层，IEEE 754 规定 **NaN 参与任何比较恒为 false**，`if (score < best)` 对 NaN 永远不成立，best 保持初始值 DBL_MAX、best_key 保持 NULL；",
          "",
          "代码层，使用前未判空——best_key 本就是「有无候选命中」的有效性哨兵。三层缺一不可：没有 ABI 错位就不会产生 NaN，没有 NaN 语义哨兵就会被覆盖，没有代码缺陷就不会崩溃。",
          "",
          "确定性推论：崩溃前有浮点运算的搜索/选择代码，先怀疑 NaN 路径；防御写法 = 比较前 `isfinite` 过滤 + 解引用前判空（`if (best_key)`）；排查信号 = 值异常（NaN/极大）而不是逻辑错误；判断路径：日志里出现 NaN/Inf 或「第一次就崩」+ 信创 ARM 环境 = ABI 层先查。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键（代码兜底）：过滤非有限值 + 哨兵判空，任何平台都该做",
          "# if (!isnan(s) && !isinf(s) && s < best) { ... }  且  if (best_key == NULL) { 走错误路径 }",
          "```",
          "",
          "环境根治见第 2 步（软浮点 ABI 重编译）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 代码兜底（任何平台都该做） — **必做**",
          "",
          "浮点比较前过滤非有限值：",
          "",
          "```c",
          "if (!isnan(s) && !isinf(s) && s < best) { best = s; best_key = &cands[i]; }",
          "```",
          "",
          "哨兵指针使用前判空：",
          "",
          "```c",
          "if (best_key == NULL) { /* 走错误路径/返回\"无结果\" */ }",
          "```",
          "",
          "可加日志输出 `best` 值——若出现 DBL_MAX 说明比较从未成功，直接指向浮点层问题。",
          "",
          "### 2. 环境根治：确认浮点 ABI 匹配 — **必做**",
          "",
          "信创软浮点环境用 `-msoft-float` 重新编译目标程序（文章解法）。排查：",
          "",
          "```bash",
          "readelf -A <二进制> | grep -i vfp",
          "ldd <二进制>",
          "grep -i fpu /proc/cpuinfo",
          "```",
          "",
          "看 ARM `Tag_ABI_VFP_args` 标记、依赖是否混入硬浮点库、CPU 是否真无 FPU。",
          "",
          "### 3. 警惕软浮点库行为 — **必做**",
          "",
          "软浮点/FTZ 实现常把 denormal 冲成 0（实测 `DBL_MIN/2 == 0`），极小值比较/分支结果与硬浮点不同；同一套代码在不同 ABI 下行为不一致时，优先怀疑浮点 ABI 而非业务逻辑。"
        ],
        "怎么确认修好了": [
          "",
          "修复版同输入走错误路径不崩溃（实测输出形态）：",
          "",
          "```text",
          "isnan 过滤 + best_key == NULL 守卫 → 同输入走错误路径，不崩溃（实测 2）",
          "```",
          "",
          "bug 版：NaN 分数数组 → best 保持 DBL_MAX、best_key 保持 NULL → 解引用 SIGSEGV（实测 1，与文章 dump 逐项一致）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 无 FPU 的 ARM64 | 软/硬浮点 ABI 错位（文章场景），重编译 |",
          "| 任何平台 | NaN 参与比较恒 false，代码兜底必做 |",
          "| denormal/下溢 | 软浮点 FTZ+DAZ 下 `DBL_MIN/2 == 0`（实测 3） |",
          "| 排查 | readelf Tag_ABI_VFP_args / ldd / /proc/cpuinfo |",
          "| 未实测项 | x86_64 WSL 无法复现 ARM 软浮点 ABI，用 NaN 模拟同一机制 |",
          ""
        ],
        "症状": [
          "",
          "真实案例：MariaDB 5.5.64 在国产 ARM64 服务器（信创早期、CPU 无 FPU）上偶发崩溃，SIGSEGV s=11；gdb 现场 `best_key = 0x0`（空指针）+ `best = DBL_MAX`（1.797e+308 哨兵初值，从未被任何候选覆盖）。",
          "",
          "泛化特征：任何「用 `DBL_MAX` 做最优初值 + 指针记录最优项」的搜索/选择代码，一旦所有比较都不成立（NaN 参与、FPU 指令异常、软硬浮点 ABI 错位），哨兵不被覆盖 → 指针保持 NULL → 解引用即崩溃。看起来\"偶发\"，因为只有数据/环境触发到比较恒 false 时才发生，平时一切正常。",
          ""
        ],
        "原因": [
          "",
          "1. **环境根因（文章结论）**：信创早期 ARM64 CPU 无 FPU/浮点指令异常 → 系统按软浮点 ABI（`-msoft-float`）编译运行；MariaDB 二进制按硬浮点 ABI 编译。软浮点程序加载硬浮点二进制/库 → 浮点寄存器约定与运算结果错位 → 浮点值被读成 NaN/错误值。",
          "2. **机制根因（通用）**：IEEE 754 下 **NaN 参与任何比较恒为 false**（`NaN < x`、`NaN >= x` 都不成立）。`if (score < best)` 恒 false → `best` 保持 DBL_MAX、`best_key` 保持 NULL。",
          "3. **代码缺陷**：使用前未判空/未校验——`best_key` 本身就是\"有无候选命中\"的有效性哨兵，但代码直接解引用，没有 `if (!best_key)` 守卫。",
          ""
        ],
        "解法": [
          "",
          "1. **根治（环境）**：确认浮点 ABI 匹配。信创软浮点环境用 `-msoft-float` 重新编译目标程序（文章解法）；排查：`readelf -A <二进制>` 看 ARM `Tag_ABI_VFP_args` 标记、`ldd` 检查依赖是否混入硬浮点库、`dmesg | grep -i fpu` / `grep -i fpu /proc/cpuinfo` 确认 CPU 是否真无 FPU。",
          "2. **兜底（代码，任何平台都该做）**：",
          "   - 浮点比较前过滤非有限值：`if (!isnan(s) && !isinf(s) && s < best)`——NaN/Inf 不参与\"最优\"选择；",
          "   - 哨兵指针使用前判空：`if (best_key == NULL) { 走错误路径/返回\"无结果\" }`，绝不直接解引用；",
          "   - 可加断言辅助排查：日志输出 `best` 值——若出现 DBL_MAX 说明比较从未成功，直接指向浮点层问题。",
          "3. **警惕软浮点库行为**：软浮点/FTZ 实现常把 denormal 冲成 0（见验证证据 3），极小值比较/分支结果与硬浮点不同；同一套代码在不同 ABI 下行为不一致时，优先怀疑浮点 ABI 而非业务逻辑。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机为 x86_64 WSL，无法直接复现 ARM 软浮点 ABI（如实标注）；用 NaN 参与比较（IEEE 下 `NaN < x` 恒 false）模拟\"比较恒不成立\"的同一机制。",
          "- 实测 1（bug 版）：C 程序用 NaN 分数数组做最优选择 → `best` 保持 DBL_MAX、`best_key` 保持 NULL → 解引用 SIGSEGV；gdb 现场 `best = 1.7976931348623157e+308`（DBL_MAX）、`best_key = 0x0`、`cands[0].score = nan`——与文章 dump 逐项一致。",
          "- 实测 2（修复版）：`isnan` 过滤 + `best_key == NULL` 守卫，同输入走错误路径，不崩溃。",
          "- 实测 3（denormal/下溢）：默认 FPU `DBL_MIN/2` 保留为 denormal（1.112537e-308，≠0）；开启 FTZ+DAZ（软浮点库常见）后 `DBL_MIN/2 == 0`——软浮点行为差异会真实改变比较/分支结果。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/float-crash",
        "agent": "https://trove.xin/agent/problems/float-crash"
      }
    },
    {
      "id": "function-calling-guard",
      "title": "LLM Function Calling 不可控？AI 建议的危险调用必须过白名单（本地 qwen 实测）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "ollama",
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://juejin.cn/post/7677837705736618035",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "给 LLM 接上工具调用（Function Calling）后，你发现\"AI 建议\"并不总是靠谱：用户说\"把 ORD123 删掉\"，模型真的返回 delete_order 调用建议；多轮对话里它可能重复查同一个订单；你也不知道它到底调了哪些函数。这不是模型坏了——Function Calling 的本质就是\"AI 只建议、你执行\"，但**无脑执行 AI 的建议等于把控制权又交回 AI**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- **AI 只建议不执行**：Function Calling 返回函数名+参数，执行权在你手上——这是它的优点，也是责任：不校验就执行 = 白拿这个设计。",
          "- **模型会被诱导**：实测（qwen2.5-0.5b 本地）用户说\"把 ORD123 删掉，客户要求取消\"，模型立刻建议 delete_order。小模型和大模型都可能被一句话带偏。",
          "- **模型无状态**：不维护对话历史，多轮里它不知道\"ORD123 查过了\"，\"那 ORD456 呢？\"会被理解成再查 ORD123。",
          "",
          "> 实测环境：ollama 0.33.2 + qwen2.5-0.5b-instruct-q4_k_m（491MB，hf-mirror 下载 89.6s），RTX 5070 Ti，2026-08-30。",
          "",
          "Function Calling 的契约是「模型只返回函数名和参数，执行权在调用方」——模型本身不执行任何操作。这个设计给了调用方全部控制权，也把全部责任留给调用方：不校验就执行 = 放弃设计的保护。两个模型侧特性加剧风险：①模型可被诱导（一句话带偏，小模型大模型都中招）；②模型默认无状态（不维护调用历史，多轮里不知道之前查过什么）。",
          "",
          "确定性推论：把 Function Calling 当「不可信输入」处理——执行前必须校验（参数白名单、权限检查、人工确认），不能因为「模型说的」就执行；敏感操作（删除/转账/覆盖）设计成二次确认或 require_human；多轮场景自己维护上下文（把之前的调用结果放进对话），别指望模型记住；判断路径：风险来自「执行」而不是「建议」，防线必须在调用方。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 执行前必过校验层：白名单（最小可用版）",
          "ALLOWED=\"query_order\"          # 只允许只读函数",
          "if [ \"$fn\" != \"$ALLOWED\" ]; then",
          "  echo \"拦截: $fn 不在白名单\" && exit 1",
          "fi",
          "# 危险函数（delete/refund/update）即使放行也要二次确认",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 3 步闭环（先跑通再谈安全） — **必做**",
          "",
          "定义 tools（函数名 + 描述 + 参数 JSON Schema）→ 把用户问题发给模型 → 模型返回 tool_calls → **你执行** → 把结果作为 tool 消息喂回 → 模型生成最终回答。",
          "",
          "### 2. 执行前加校验层（白名单 + 参数 + 危险操作确认） — **必做**",
          "",
          "实测复现：tools 同时提供 query_order（只读）和 delete_order（删除），用户一句\"把 ORD123 删掉\"模型就建议 delete_order。校验层必须做三件事：",
          "",
          "- 白名单：只允许声明过的安全函数被真正执行；",
          "- 参数校验：如 order_id 必须匹配 ORD+数字 格式，防注入式参数；",
          "- 危险操作二次确认：delete/refund/update 类函数标记为危险，需要人工确认才执行。",
          "",
          "### 3. 多轮对话维护历史 + 调用记录 — **必做**",
          "",
          "把完整 messages（user + assistant tool_calls + tool 结果）持续喂回。实测对比：带历史时第二轮\"那 ORD456 呢？\"正确调用 query_order(ORD456)；不维护历史则会重复查 ORD123。"
        ],
        "怎么确认修好了": [
          "",
          "```text",
          "# 实测输出（2026-08-30）",
          "AI 建议调用: delete_order {\"order_id\": \"ORD123\"}",
          "白名单校验: 拦截! delete_order 不在白名单 {\"query_order\"}",
          "",
          "# 多轮：",
          "第1轮 AI 调用: query_order {\"order_id\": \"ORD123\"} → 已发货，顺丰 SF123456",
          "第2轮 AI 调用: query_order {\"order_id\": \"ORD456\"}（带历史，应查 ORD456）",
          "```",
          "",
          "诱导危险调用被拦截 + 多轮正确指代 = 通过。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 模型大小 | 0.5B 小模型即可复现\"诱导危险调用\"；大模型更\"听话\"但同样可能被诱导，校验层不分模型必须上 |",
          "| 调用方式 | OpenAI 兼容 chat 接口返回 tool_calls；本地 ollama 与云端 API 结构一致，校验代码可复用 |",
          "| 与 Agent 对比 | 生产用 Function Calling（你控制执行），开发验证可用 Agent；Agent 全自动不可控性更高 |",
          "| SDK 差异 | langchain/原生 SDK 只是封装不同，tool_calls 结构一致；参数解析注意 ollama 返回 dict 而部分 SDK 返回 JSON 字符串 |",
          "| 无状态边界 | 不维护历史的模型必然重复调用；上下文长度有限时只回放最近 N 轮 + 调用记录 |",
          "| 国产模型 | qwen 系对 tools 支持好（实测）；部分老模型不支持 tools 时只能提示词硬编码 JSON 输出，校验层更要兜底 |",
          ""
        ],
        "症状": [
          "",
          "AI 建议调用危险函数（删除/退款/更新）被无脑执行；多轮对话重复调用同一函数或张冠李戴；Function Calling 接入后不可控、不可观测。",
          ""
        ],
        "原因": [
          "",
          "Function Calling 设计上\"AI 建议、你执行\"，执行前缺少校验层（白名单/参数/危险操作确认）时控制权实际交回 AI；模型无状态，不维护对话历史与调用记录导致重复调用。",
          ""
        ],
        "解法": [
          "",
          "3 步闭环跑通后：执行前加白名单 + 参数校验 + 危险操作二次确认；多轮对话维护完整 messages（含 assistant tool_calls 与 tool 结果）持续喂回。",
          ""
        ],
        "验证证据": [
          "",
          "实测复现日志（2026-08-30）（2026-08-30 本地实测：3 步闭环通过；诱导删除 → AI 建议 delete_order 被白名单拦截；带历史多轮正确查 ORD456）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/function-calling-guard",
        "agent": "https://trove.xin/agent/problems/function-calling-guard"
      }
    },
    {
      "id": "git-crlf",
      "title": "git 换行符混乱——diff 全部文件显示修改 / warning: LF will be replaced by CRLF / CRLF will be replaced by LF",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "encoding-format",
      "category_label": "编码与格式",
      "applies_to": [
        "git"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/nhkiiim/TIL/issues/6",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "改动一行代码，`git diff` 却把整个文件标成修改（每行都是红+/绿-）；`git add` 时冒 `warning: LF will be replaced by CRLF`（或反向 `CRLF will be replaced by LF`）；同一仓库在 Windows 和 Linux/WSL 两边 checkout 出来\"看起来一样\"但 diff 永远不干净；",
          "",
          "脚本在 Windows 上 clone 下来后报 `\\r` 相关执行错误。",
          "",
          "**换行符策略没统一**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Windows 文本默认 CRLF（\\r\\n），Linux/macOS 默认 LF（\\n）。git 的 `core.autocrlf` 控制转换，而**入库格式取决于首次提交时各人的配置**：",
          "",
          "| 配置 | 行为 | 适用 |",
          "|---|---|---|",
          "| autocrlf=false | 不做转换，CRLF 直接入库 | Windows 旧默认——跨平台仓库的坑源 |",
          "| autocrlf=true | 提交转 LF、检出转 CRLF | Windows 推荐 |",
          "| autocrlf=input | 提交转 LF、检出不转 | Linux 推荐 |",
          "",
          "多人各配各的，仓库里 CRLF/LF 混合入库，就是\"谁提交谁踩坑\"的源头。`warning: LF will be replaced by CRLF` 本身无害，但出现 = 行尾策略没统一。",
          "",
          "行尾转换发生在「写出/读入」两个方向：`core.autocrlf` 控制的是**提交时把工作区 CRLF 转成 LF 入库、检出时按配置转回**——它只改文本流的字节，不识别文件类型，所以二进制文件在 autocrlf=true 下也可能被误转（.gitattributes 的 `-text` 就是用来豁免的）。",
          "",
          "仓库里最终存什么，取决于**每个提交者自己的配置**：autocrlf=false 的人把 CRLF 原样提交，autocrlf=true 的人提交时转 LF——同一仓库两种字节并存，git 的 diff 就会显示整行改动。",
          "",
          "确定性推论：`LF will be replaced by CRLF` 警告 = 检出时发生了转换，说明配置已生效但策略未统一；跨平台仓库应该在 `.gitattributes` 里用 `* text=auto` + 显式声明（`*.sh text eol=lf` 等）把策略写进仓库，而不是依赖每个人的本地配置——配置是每台机器自己的，属性是跟着仓库走的。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：仓库根目录加 .gitattributes 并重规范化（一劳永逸）",
          "printf '* text=auto eol=lf\\n' > .gitattributes && git add .gitattributes && git add --renormalize . && git commit -m \"normalize line endings\"",
          "```",
          "",
          "之后 `git ls-files --eol` 应显示 `i/lf w/lf`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 症状无害时（只是 warning、diff 没乱） — **必做**",
          "",
          "确认全队统一 `core.autocrlf`（Windows 用 `true`，Linux/mac 用 `input`），warning 会消失。",
          "",
          "### 2. diff 全红/已混入库（推荐，一劳永逸） — **必做**",
          "",
          "仓库根目录加 `.gitattributes`：",
          "",
          "```text",
          "* text=auto eol=lf",
          "```",
          "",
          "然后重规范化（把历史里已入库的 CRLF 全部转成 LF）：",
          "",
          "```bash",
          "git add .gitattributes",
          "git add --renormalize .",
          "git commit -m \"normalize line endings\"",
          "```",
          "",
          "### 3. 只想管特定类型 — **必做**",
          "",
          "`.gitattributes` 写 `*.sh text eol=lf`、`*.bat text eol=crlf` 等，其余不动。",
          "",
          "### 4. 避免再犯 — **必做**",
          "",
          "新仓库先在根目录提交 `.gitattributes`（`* text=auto`）再写内容；不要用 `autocrlf=false` 做跨平台仓库的默认（那会把 CRLF 存进仓库）。"
        ],
        "怎么确认修好了": [
          "",
          "入库格式统一（实测输出形态）：",
          "",
          "```text",
          "git ls-files --eol → 全部 i/lf w/lf，无 warning",
          "git diff → 只显示真实改动（加 1 行只报 1 insertion，实测）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows | autocrlf=true（提交转 LF、检出转 CRLF） |",
          "| Linux/mac | autocrlf=input |",
          "| 已混入库 | .gitattributes + `git add --renormalize .`（实测修复） |",
          "| 特定文件 | `*.sh text eol=lf`、`*.bat text eol=crlf` |",
          "| 新仓库 | 先提交 .gitattributes 再写内容 |",
          ""
        ],
        "症状": [
          "",
          "- 改动一行代码，`git diff` 却把整个文件标成修改（每行都是红+/绿-）；",
          "- `git add` 时冒 `warning: in the working copy of 'xxx', LF will be replaced by CRLF the next time Git touches it`（或反向 `CRLF will be replaced by LF`）；",
          "- 同一仓库在 Windows 和 Linux/WSL 两边 checkout 出来内容「看起来一样」但 diff 永远不干净；",
          "- 脚本（.sh/.py）在 Windows 上 clone 下来后报 `\\r` 相关执行错误（CRLF 进入脚本）。",
          "",
          "特征：纯文本文件；报错带「换行符」字样；Windows/Linux 双环境协作的仓库最常见。",
          ""
        ],
        "原因": [
          "",
          "Windows 文本默认 CRLF（\\r\\n），Linux/macOS 默认 LF（\\n）。git 的 `core.autocrlf` 控制转换，而**入库格式取决于首次提交时各人的配置**：",
          "",
          "- `autocrlf=false`（Windows 旧版 git 默认、很多老教程推荐）：提交时不做任何转换，CRLF 直接被存进仓库——后续任何 LF 工作区一比对就全行不同，diff 全红；",
          "- `autocrlf=true`（Windows 推荐）：提交转 LF、检出转 CRLF；",
          "- `autocrlf=input`（Linux 推荐）：提交转 LF、检出不转。",
          "",
          "多人各配各的（Windows 默认 false + Linux 默认 input 或 false），仓库里 CRLF/LF 混合入库，就是「谁提交谁踩坑」的源头。`warning: LF will be replaced by CRLF` 只是提示 git 会在下次操作时转换，本身无害，但它出现 = 行尾策略没统一。",
          ""
        ],
        "解法": [
          "",
          "1. **症状无害时**（只是 warning、diff 没乱）：确认全队统一 `core.autocrlf`（Windows 用 `true`，Linux/mac 用 `input`），warning 会消失。",
          "2. **diff 全红/已混入库**（推荐，一劳永逸）：",
          "   仓库根目录加 `.gitattributes`：",
          "   ```",
          "   * text=auto eol=lf",
          "   ```",
          "   然后重规范化（把历史里已入库的 CRLF 全部转成 LF）：",
          "   ```",
          "   git add .gitattributes",
          "   git add --renormalize .",
          "   git commit -m \"normalize line endings\"",
          "   ```",
          "   之后 `git ls-files --eol` 应显示 `i/lf w/lf`（入库/工作区都是 LF）。",
          "3. **只想管特定类型**：`.gitattributes` 写 `*.sh text eol=lf`、`*.bat text eol=crlf` 等，其余不动。",
          "4. **避免再犯**：新仓库先在根目录提交 `.gitattributes`（`* text=auto`）再写内容；不要用 `autocrlf=false` 做跨平台仓库的默认（那会把 CRLF 存进仓库）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，git 2.53.0）：",
          "  - 复现 1（diff 全红）：`autocrlf=false` 提交 CRLF 文件（`git ls-files --eol` 显示 `i/crlf w/crlf`）→ 工作区改成 LF 后 `git diff` 把 2 行显示成 3 删 2 加（全行变化）；",
          "  - 复现 2（warning 正向）：`autocrlf=true` 时 add LF 文件 → `warning: in the working copy of 'lf.txt', LF will be replaced by CRLF the next time Git touches it`；",
          "  - 复现 3（warning 反向）：`autocrlf=input` 时 add CRLF 文件 → `warning: ... CRLF will be replaced by LF ...`；",
          "  - 修复：根目录 `.gitattributes`（`* text=auto eol=lf`）+ `git add --renormalize .` → 入库格式全部 `i/lf w/lf`，无 warning；",
          "  - 修复后 diff 只显示真实改动（加 1 行只报 1 insertion）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/git-crlf",
        "agent": "https://trove.xin/agent/problems/git-crlf"
      }
    },
    {
      "id": "git-dubious-ownership",
      "title": "git 报 fatal: detected dubious ownership in repository at（仓库属主与当前用户不一致）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "git"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/lusa/p/18489485",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "对某个仓库执行任意 git 命令（git status / git log / git pull…）直接报 `fatal: detected dubious ownership in repository at '<路径>'`，还教你 `git config --global --add safe.directory <路径>`。仓库能正常打开、文件也都在，但所有 git 命令全挂。",
          "",
          "最常出现在：双系统 Ubuntu 挂载 Windows NTFS 硬盘、U 盘/移动硬盘拷贝的仓库、WSL 访问 Windows 侧目录、root/sudo 创建的仓库——**仓库文件属主和当前登录用户不一致**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "git 2.35.2 起引入的安全机制（修复 CVE-2022-24765 等恶意仓库攻击）：仓库所在目录的属主若与当前用户不一致，git 拒绝执行——防止攻击者构造的 .git/config（含自定义命令钩子）在别的用户环境里被意外信任执行。属主不一致的常见来源：",
          "",
          "| 来源 | 说明 |",
          "|---|---|",
          "| NTFS/exFAT 挂载盘 | 文件系统不支持 POSIX 属主，盘上文件都显示为挂载用户（常是 root），chown 改了也不生效 |",
          "| 多用户共享目录 | 别人（或 root）建的仓库，你直接访问 |",
          "| U 盘/压缩包拷贝 | 保留原机器的 uid/gid，与当前机器对不上 |",
          "",
          "`dubious ownership` 是 git 2.35.2 起的**信任边界检查**：git 在执行仓库操作前，校验仓库根目录的属主是否等于当前用户（或当前用户所属组），不一致就拒绝——防御的是「攻击者把 .git/config 放进你名义上可写的目录，用自定义钩子（core.hooksPath / alias）在你环境下执行任意命令」这类 CVE-2022-24765 攻击。",
          "",
          "属主不一致≠权限不够：NTFS/exFAT 这类不支持 POSIX 属主的文件系统上，所有文件都显示为挂载者（root），你 root 之外的用户访问必触发。",
          "",
          "确定性推论：看到 `dubious ownership` 先 `ls -ld <仓库>` 看属主，再决定——单用户可信环境用 `git config --global --add safe.directory <路径>` 显式声明信任（这是「我确认这个目录安全」的声明，不是关掉检查）；多用户共享目录应该修属主而不是堆 safe.directory；",
          "",
          "挂载盘上 chown 无效，safe.directory 是唯一正解。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：按错误提示加白名单（最通用，挂载盘也有效）",
          "git config --global --add safe.directory <仓库绝对路径>",
          "```",
          "",
          "注意：白名单**按目录登记、不递归**——同盘其他仓库还要再 add 一次。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. safe.directory 白名单（最通用） — **必做**",
          "",
          "```bash",
          "git config --global --add safe.directory <仓库绝对路径>",
          "```",
          "",
          "仓库很多时可用 `git config --global --add safe.directory '*'` 全局信任（省事但信任所有目录，不推荐在共享机器上用）。",
          "",
          "### 2. chown 改属主（仅 Linux 文件系统） — **必做**",
          "",
          "```bash",
          "sudo chown -R $(id -u):$(id -g) <仓库路径>",
          "```",
          "",
          "对 NTFS/exFAT 挂载盘无效（文章作者实测无效），挂载盘请用方案 1 或 3。",
          "",
          "### 3. 双系统 NTFS 挂载源头修复 — **必做**",
          "",
          "编辑 `/etc/fstab` 在挂载项加 `uid=1000,gid=1000`（1000 换成 `id <用户名>` 查到的值）后重启；或 Ubuntu 磁盘工具里关闭默认挂载选项、在挂载参数里加同样的 uid/gid。盘上所有仓库天然属主一致。",
          "",
          "### 4. 只想临时看一眼 — **必做**",
          "",
          "直接读文件不跑 git 命令；或把仓库重新 clone 到自己目录下（新 clone 属主就是你自己）。"
        ],
        "怎么确认修好了": [
          "",
          "白名单后 git 命令恢复（实测输出形态）：",
          "",
          "```text",
          "nobody 用户 git status/log → 退出码 0（白名单后实测）",
          "chown 后不配白名单直接访问 → 成功（Linux 文件系统上 chown 有效，实测）",
          "```",
          "",
          "对照组：白名单只覆盖已登记目录——登记目录下新建子仓库仍报错（逐个登记、不递归，实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| NTFS/exFAT 挂载盘 | safe.directory 有效；chown 无效（文章实测） |",
          "| 多用户共享 | 白名单或 chown |",
          "| U 盘/压缩包 | 白名单或重新 clone |",
          "| WSL 访问 Windows 侧 | 白名单最省事 |",
          "| 不递归 | 每个仓库各 add 一次 |",
          ""
        ],
        "症状": [
          "",
          "对某个仓库执行任意 git 命令（git status / git log / git pull…）直接报：",
          "",
          "```",
          "fatal: detected dubious ownership in repository at '<仓库绝对路径>'",
          "To add an exception for this directory, call:",
          "",
          "\tgit config --global --add safe.directory <仓库绝对路径>",
          "```",
          "",
          "特征：仓库能正常打开、文件也都在，但所有 git 命令全挂；错误里点名的路径是仓库根目录；最常出现在双系统 Ubuntu 挂载 Windows NTFS 硬盘、U 盘/移动硬盘拷贝的仓库、WSL 访问 Windows 侧目录、或 root/sudo 创建的仓库——这些场景下仓库文件属主和当前登录用户不一致。",
          ""
        ],
        "原因": [
          "",
          "git 2.35.2 起引入的安全机制（修复 CVE-2022-24765 等恶意仓库攻击）：仓库所在目录的属主若与当前用户不一致，git 拒绝执行——防止攻击者构造的 .git/config（含自定义命令钩子）在别的用户环境里被意外信任执行。属主不一致的常见来源：",
          "",
          "1. **NTFS/exFAT 等挂载盘**：文件系统不支持 POSIX 属主，盘上所有文件在 Linux 眼里都显示为挂载用户（通常是 root），chown 改了也不生效——双系统用户最典型；",
          "2. **多用户共享目录**：别人（或 root）建的仓库，你直接访问；",
          "3. **U 盘/压缩包拷贝**：保留的是原机器的 uid/gid，与当前机器对不上。",
          ""
        ],
        "解法": [
          "",
          "按推荐顺序选：",
          "",
          "1. **safe.directory 白名单（最通用，挂载盘也有效）**：照错误提示执行 `git config --global --add safe.directory <仓库绝对路径>`。注意：白名单**按目录登记、不递归**——同盘其他仓库还要再 add 一次；仓库很多时可用 `git config --global --add safe.directory '*'` 全局信任（省事但信任所有目录，不推荐在共享机器上用）。",
          "2. **chown 改属主（仅 Linux 文件系统）**：`sudo chown -R $(id -u):$(id -g) <仓库路径>`。对 NTFS/exFAT 挂载盘无效（文章作者实测无效），挂载盘请用方案 3 或 1。",
          "3. **双系统 NTFS 挂载源头修复**：让整个盘固定属主为你的用户。编辑 /etc/fstab 在挂载项加 `uid=1000,gid=1000`（1000 换成 `id <用户名>` 查到的值）后重启；或 Ubuntu 磁盘工具里关闭默认挂载选项、在挂载参数里加同样的 uid/gid。这样盘上所有仓库天然属主一致，不再报错。",
          "4. 只想临时看一眼：直接读文件不跑 git 命令；或把仓库重新 clone 到自己目录下（新 clone 属主就是你自己）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，git 2.53.0）：",
          "  - root 建仓库并提交，改用 nobody 用户访问 → 完整复现 `fatal: detected dubious ownership in repository at '/tmp/dubious-repo'` + 白名单提示，退出码 128，与文章报错逐字一致；",
          "  - 修复 A：nobody 执行 `git config --global --add safe.directory /tmp/dubious-repo` → 再次 git status/log 成功（退出码 0）；",
          "  - 修复 B：`chown -R nobody:nogroup` 仓库后不配白名单直接访问 → 成功（退出码 0，证明 Linux 文件系统上 chown 有效）；",
          "  - 对照组：白名单只覆盖已登记的目录——在登记目录下新建子仓库仍报同样错误，证明「逐个登记、不递归」；",
          "  - NTFS 挂载 uid/gid 方案本机无 NTFS 挂载盘，未能实测（文章作者实测有效，属于环境源头修复）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/git-dubious-ownership",
        "agent": "https://trove.xin/agent/problems/git-dubious-ownership"
      }
    },
    {
      "id": "github-clone-fail",
      "title": "git clone 公开仓库报 fatal: Could not read from remote repository / Please make sure you have the correct access rights and the repository exists（网络正常）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "git"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/security-hyacinth/p/19771648",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "clone 一个**公开仓库**（浏览器能打开、curl 也通），git 却报 `ERROR: Your account is suspended` + `fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.`",
          "",
          " 文案极具迷惑性：GitHub 对失效账号返回的 403/404 是同一句，连公开仓库也一样，容易误判为网络/仓库问题。",
          "",
          "**网络是通的，问题在 git 配置/凭据层。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "最常见：**全局 git 配置把 HTTPS 重写成了 SSH**（老教程教「强制走 SSH」留下的配置）：",
          "",
          "```text",
          "url.git@github.com:.insteadof=https://github.com/",
          "```",
          "",
          "重写后所有 `https://github.com/...` 的 clone 实际走 SSH。SSH 认证用的是本机 SSH key / gh 凭据助手绑定的账号——账号状态异常（被封禁/风控、key 被删、token 失效）时，连**匿名即可访问的公开仓库**也被拒绝。次要来源：gh CLI 凭据助手向 HTTPS 注入失效/被封的 token。",
          "",
          "`url.<new>.insteadOf <old>` 是 git 的 **URL 重写层**：git 在发起任何远程访问前，先查 config 里的重写规则，把匹配的地址整体替换——它作用在「寻址层」，所以重写后你看到的命令还是 `git clone https://...`，实际走的却是 SSH。",
          "",
          "SSH 通道的认证是「本机 SSH key / 凭据助手」与 GitHub 账号的绑定：账号被封禁/风控、key 被删、token 失效，认证就失败——哪怕目标仓库是公开的（公开仓库 HTTPS 匿名可读，SSH 却要求凭据）。",
          "",
          "确定性推论：公开仓库报权限错 = 先查重写规则（`git config --get-all url.*.insteadof`）和代理残留，再查凭据状态（`ssh -T git@github.com`、`gh auth status`）；",
          "",
          "`unset` 或删除 insteadOf 规则后 HTTPS 匿名通道恢复，就能区分是「通道被改写」还是「凭据真失效」。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：匿名 HTTPS 验证（不改配置，一锤定音）",
          "GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git",
          "```",
          "",
          "能返回 refs → 配置问题实锤；然后删重写规则：`git config --global --unset-all \"url.git@github.com:.insteadof\"`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先分层 — **必做**",
          "",
          "```bash",
          "curl -s -o /dev/null -w \"%{http_code}\" --max-time 10 https://github.com/",
          "```",
          "",
          "返回 200/301 → 网络通，问题在 git 配置/凭据层；超时或 000 → 网络层（见第 6 步）。",
          "",
          "### 2. 查重写规则 — **必做**",
          "",
          "```bash",
          "git config --global --get-regexp 'url\\..*\\.insteadof'",
          "```",
          "",
          "看到 `url.git@github.com:.insteadof https://github.com/` 就是元凶。",
          "",
          "### 3. 匿名 HTTPS 验证（不改任何配置，一锤定音） — **必做**",
          "",
          "```bash",
          "GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git",
          "```",
          "",
          "能返回 refs → 配置问题实锤；同样失败 → 才是网络/远端问题。",
          "",
          "### 4. 修复：删除重写规则 — **必做**",
          "",
          "```bash",
          "git config --global --unset-all \"url.git@github.com:.insteadof\"",
          "```",
          "",
          "公开仓库走匿名 HTTPS 即可。若确实需要 SSH 通道（私有仓库/写权限），去处理账号本身：换有效 SSH key 或 `gh auth login` 重新授权——不要保留重写规则让所有 HTTPS 都踩坑。",
          "",
          "### 5. 凭据助手脏凭据 — **必做**",
          "",
          "```bash",
          "git config --global --list | grep credential",
          "gh auth status",
          "```",
          "",
          "看有没有 `!/usr/bin/gh auth git-credential`；失效则 `gh auth logout` 后 `gh auth login`（公开仓库其实不需要任何凭据）。",
          "",
          "### 6. 若第 1 步确认是网络层 — **必做**",
          "",
          "配代理 `git config --global http.https://github.com.proxy http://127.0.0.1:<端口>`；或临时走加速前缀 `git clone https://gh-proxy.com/https://github.com/<owner>/<repo>.git`（加速站有生命周期，用前先 curl 测）。"
        ],
        "怎么确认修好了": [
          "",
          "修复后 clone 成功（实测输出形态）：",
          "",
          "```text",
          "git config --global --unset-all url.git@github.com:.insteadof",
          "git clone --depth 1 https://github.com/octocat/Hello-World.git → exit 0，产物完整（实测）",
          "```",
          "",
          "匿名 HTTPS ls-remote 正常返回全部 refs（实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 网络层（国内直连） | 超时/重置，配代理或加速前缀 |",
          "| HTTPS→SSH 重写 | unset-all 删除（实测元凶） |",
          "| gh 凭据助手 | gh auth logout/login 重新授权 |",
          "| 账号被封 | SSH 侧账号状态异常，处理账号本身 |",
          "| 公开仓库 | 匿名 HTTPS 即可，不需要任何凭据 |",
          ""
        ],
        "症状": [
          "",
          "clone 一个**公开仓库**（浏览器能打开、curl 也通），git 却报：",
          "",
          "```",
          "ERROR: Your account is suspended. Please visit https://support.github.com for more information.",
          "fatal: Could not read from remote repository.",
          "",
          "Please make sure you have the correct access rights",
          "and the repository exists.",
          "```",
          "",
          "特征：",
          "",
          "- 网络是通的：网页能打开、`curl https://github.com/` 返回 200，但 git 一律失败（exit 128）；",
          "- 报错指向「权限」而不是「网络」；`fatal: unable to access ... Timed out` 类报错属网络层，不在本篇；",
          "- 之前能 clone、某天突然全挂，或换机器/换账号后开始出现；",
          "- 文案极具迷惑性：GitHub 对失效账号返回的 403/404 是同一句 \"Please make sure you have the correct access rights and the repository exists\"，连公开仓库也一样，容易误判为网络/仓库问题。",
          ""
        ],
        "原因": [
          "",
          "最常见：**全局 git 配置把 HTTPS 重写成了 SSH**（老教程教「强制走 SSH」留下的配置）：",
          "",
          "```",
          "url.git@github.com:.insteadof=https://github.com/",
          "```",
          "",
          "重写后所有 `https://github.com/...` 的 clone 实际走 `git@github.com:...`（SSH）。SSH 认证用的是本机 SSH key / gh 凭据助手绑定的账号——一旦该账号状态异常（被封禁/风控、key 被删、token 失效），连**匿名即可访问的公开仓库**也被拒绝。",
          "",
          "次要来源：`credential.https://github.com.helper=!/usr/bin/gh auth git-credential`（gh CLI 凭据助手）向 HTTPS 注入失效/被封的 token，同样报 access rights。",
          ""
        ],
        "解法": [
          "",
          "1. **先分层**：`curl -s -o /dev/null -w \"%{http_code}\" --max-time 10 https://github.com/` 返回 200/301 → 网络通，问题在 git 配置/凭据层；超时或 000 → 网络层（见第 6 条）。",
          "2. **查重写规则**：`git config --global --get-regexp 'url\\..*\\.insteadof'`。看到 `url.git@github.com:.insteadof https://github.com/` 就是元凶。",
          "3. **匿名 HTTPS 验证**（不改任何配置，一锤定音）：",
          "   `GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git`",
          "   能返回 refs → 配置问题实锤；同样失败 → 才是网络/远端问题。",
          "4. **修复**：删除重写规则（公开仓库走匿名 HTTPS 即可）：",
          "   `git config --global --unset-all \"url.git@github.com:.insteadof\"`",
          "   然后重新 clone 验证。若确实需要 SSH 通道（私有仓库/写权限），去处理账号本身：换有效 SSH key 或 `gh auth login` 重新授权——不要保留重写规则让所有 HTTPS 都踩坑。",
          "5. **凭据助手脏凭据**：`git config --global --list | grep credential` 看有没有 `!/usr/bin/gh auth git-credential`；`gh auth status` 检查账号状态，失效则 `gh auth logout` 后 `gh auth login`（公开仓库其实不需要任何凭据）。",
          "6. **若第 1 步确认是网络层**（国内直连超时/重置）：配代理 `git config --global http.https://github.com.proxy http://127.0.0.1:<端口>`（按代理端口，`http://`/`socks5://` 均可）；或临时走加速前缀 `git clone https://gh-proxy.com/https://github.com/<owner>/<repo>.git`——2026-08-29 校园网实测 gh-proxy.com / ghfast.top / ghproxy.homeboyc.cn 存活（加速站有生命周期，用前先 curl 测）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，校园网无代理，git 2.53.0，公开仓库 octocat/Hello-World）：",
          "  - 网络层：curl github.com → 200；",
          "  - 匿名 HTTPS（`GIT_CONFIG_GLOBAL=/dev/null`）：ls-remote 返回全部 refs（成功）；",
          "  - 默认配置：同命令 exit 128，报 `ERROR: Your account is suspended` + `Please make sure you have the correct access rights and the repository exists`；",
          "  - 显式 SSH（`git@github.com:...`）：同样 exit 128 → 证明 HTTPS 被全局重写规则转到了 SSH，而 SSH 绑定的账号已被 GitHub 封禁；",
          "  - 修复验证：备份全局配置 → `git config --global --unset-all url.git@github.com:.insteadof` → `git clone --depth 1` 成功（exit 0，产物完整）→ 恢复原配置。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/github-clone-fail",
        "agent": "https://trove.xin/agent/problems/github-clone-fail"
      }
    },
    {
      "id": "github-direct-connect-fail",
      "title": "GitHub 直连慢/失败（clone 被重置、443 连不上、SSH 认证、Release 下载中断）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "git"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "本站同根合并整理（2026-08-30）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "所有依赖 GitHub 的链路在国内直连都不稳：`git clone` 大仓库卡在 Cloning repository 后被重置；插件/技能 CLI 安装报 443 连不上；marketplace 克隆报 SSH 认证失败；从 Release 下 deb/AppImage 反复断连。",
          "",
          "报错五花八门，但根因就三层：**网络层（直连不稳）、配置层（git 重写规则/失效代理）、凭据层（SSH key / gh token 状态）**。",
          "",
          "先分层诊断，再动手，别对着报错瞎试。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 层 | 根因 | 典型表现 |",
          "|---|---|---|",
          "| 网络层 | 国内出口到 GitHub 直连不稳定；Release 资产实际托管在 `objects.githubusercontent.com`，是另一条更差的链路 | `Connection reset by peer`、443 连不上、下载中断、卡在 Cloning repository |",
          "| 配置层 | 老教程留下的 `url.git@github.com:.insteadof=https://github.com/` 把 HTTPS 全重写成 SSH；或失效的 `http.proxy` 残留 | 公开仓库也报权限错误；任何 clone 都被重置 |",
          "| 凭据层 | SSH key 无效/未配、gh 凭据助手注入失效 token、账号被封 | `SSH authentication failed`、`Permission denied (publickey)`、`Your account is suspended` |",
          "",
          "关键认知：**公开仓库匿名 HTTPS 即可访问，不需要任何凭据**。凡是公开仓库报\"权限错误\"，先怀疑配置层和凭据层，不要怀疑网络。",
          "",
          "三层模型对应三条独立链路：网络层是「TCP/TLS 到 github.com 与 objects.githubusercontent.com」的物理可达性，国内出口不稳定时连接在传输层被重置，跟命令无关；配置层是「寻址改写」——`insteadOf` 把 HTTPS 重写成 SSH、残留 `http.proxy` 把请求指向失效代理，作用在 git 访问任何远程之前；",
          "",
          "凭据层是「认证状态」——SSH key、gh token、账号状态决定公开仓库之外的访问是否被接受。",
          "",
          "三层是串联关系：先有可达性（网络），再按配置寻址（配置），最后凭据认证（凭据）——所以排查顺序固定，从下往上：先 curl 直连测网络、再查 insteadOf/proxy、最后验凭据。公开仓库报「权限错误」时，网络层已被排除（能连上才会走到认证），问题只可能在配置层或凭据层。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# ① 分层诊断（30 秒，一锤定音）",
          "curl -s -o /dev/null -w \"%{http_code}\\n\" --max-time 10 https://github.com/          # 200/301 = 网络通，问题在配置/凭据层",
          "GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git  # 能列 refs = 配置/凭据层实锤",
          "",
          "# ② 网络层（直连不稳）：gh-proxy 加速前缀，其余保持匿名 HTTPS",
          "git clone https://gh-proxy.com/https://github.com/<owner>/<repo>.git",
          "# 或走 codeload tarball：curl -sL https://gh-proxy.com/https://codeload.github.com/<owner>/<repo>/tar.gz/refs/tags/<tag> | tar xz",
          "",
          "# ③ 配置/凭据层（② 不适用时）：删重写规则 + 清失效代理 + gh 重新授权",
          "git config --global --unset-all \"url.git@github.com:.insteadof\"",
          "git config --global --unset http.proxy && git config --global --unset https.proxy",
          "gh auth logout && gh auth login",
          "```",
          "",
          "做完 ② 或 ③ 后重跑原命令。若 ② 的 gh-proxy 反而更慢/失败，去掉它只保留匿名 HTTPS（镜像有生命周期，用前先 `curl -sI` 测）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 分层诊断 — **必做**（30 秒，决定走哪条路）",
          "",
          "```bash",
          "curl -s -o /dev/null -w \"%{http_code}\\n\" --max-time 10 https://github.com/",
          "```",
          "",
          "- 超时 / `000` → 网络层，走第 2 步；",
          "- `200` / `301` → 网络通，继续：",
          "",
          "```bash",
          "GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git",
          "```",
          "",
          "- 能返回 refs → 配置/凭据层实锤，走第 4 步；",
          "- 同样失败 → 网络层对 git 协议限速/断连，走第 2 步。",
          "",
          "### 2. 网络层：换通道 — **按诊断结果**（网络层命中才走）",
          "",
          "三种通道按可用性排序（本市场 2026-08-28~30 实测）：",
          "",
          "```bash",
          "# a) gh-proxy 前缀（实测 13.5MB Release 资产秒级）",
          "curl -L -o pkg.deb \"https://gh-proxy.com/https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>\"",
          "",
          "# b) codeload tarball（实测 544K / 2.1s 稳定）",
          "curl -sL https://codeload.github.com/<owner>/<repo>/tar.gz/refs/tags/<tag> | tar xz",
          "",
          "# c) 显式代理（有代理时）",
          "git config --global http.https://github.com.proxy http://127.0.0.1:<端口>",
          "```",
          "",
          "tarball 下载断 → 改 git 浅克隆（`git clone --depth 1`）；clone 断 → 改 tarball。两者互换常能绕过坏链路。错峰（避开晚间高峰）与换网络（热点常能绕开）也有效。",
          "",
          "#### SSH over 443（官方通道：SSH 层被墙的出路）",
          "",
          "如果 22 端口不通但 SSH key 本身正常，GitHub 官方支持把 SSH 连接走 443 端口（HTTPS 端口，大多数防火墙放行 443 而拦 22）。",
          "",
          "```bash",
          "# ① 测 443 通道（主机名是 ssh.github.com，不是 github.com）",
          "ssh -T -p 443 git@ssh.github.com",
          "# 成功输出: Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.",
          "",
          "# ② 单次使用：直接按这个地址 clone",
          "git clone ssh://git@ssh.github.com:443/<owner>/<repo>.git",
          "",
          "# ③ 永久生效：写入 ~/.ssh/config",
          "Host github.com",
          "    Hostname ssh.github.com",
          "    Port 443",
          "    User git",
          "```",
          "",
          "首次连接会提示 `The authenticity of host '[ssh.github.com]:443 ...' can't be established`——这是 known_hosts 里还没有 `ssh.github.com` 的正常警告；指纹与 GitHub 官方公布的 ED25519 指纹一致即可选 yes。",
          "",
          "注意两点：GitHub Enterprise Server 不支持此通道；显式代理可能干扰 443 通道（有代理时优先用 gh-proxy 方案）。",
          "",
          "### 3. 网络层：安装类场景换\"不依赖 GitHub\"的路径 — **按场景**（安装类工具）",
          "",
          "- 只想用工具：`pip install <包>`（清华镜像 `-i https://pypi.tuna.tsinghua.edu.cn/simple`），不需要 clone 源码；",
          "- 技能/插件：tarball 解压后手工复制到对应目录（如 `cp -r agent-skills-0.6.7/skills ~/.claude/skills/`）；",
          "- 判断成功看产物（目录/文件就位），不要只看 exit 码（部分 CLI 失败时 exit 码仍为 0）。",
          "",
          "### 4. 配置层：查重写规则与失效代理 — **按诊断结果**（配置层命中才走）",
          "",
          "```bash",
          "git config --global --get-regexp 'url\\..*\\.insteadof'   # 看到 url.git@github.com:.insteadof = 元凶",
          "git config --global --list | grep -i proxy               # 代理残留检查",
          "git config --global --unset-all \"url.git@github.com:.insteadof\"",
          "git config --global --unset http.proxy",
          "git config --global --unset https.proxy",
          "```",
          "",
          "### 5. 凭据层：SSH 与 gh 凭据助手 — **按诊断结果**（凭据层命中才走）",
          "",
          "```bash",
          "git ls-remote git@github.com:<owner>/<repo>   # Permission denied (publickey) = SSH 层问题",
          "git config --global url.\"https://github.com/\".insteadOf \"git@github.com:\"   # 强制 HTTPS（公开仓库匿名即可）",
          "gh auth status                                # 凭据助手状态",
          "gh auth logout && gh auth login               # 重新授权",
          "```",
          "",
          "### 6. 下载后校验完整性 — **必做**（收尾）",
          "",
          "```bash",
          "du -h pkg.deb        # 对照资产页标注大小，差太多 = 断了一半，重下",
          "dpkg-deb -I pkg.deb >/dev/null && echo DEB-OK   # 结构可读 = 完整（deb 类）",
          "```",
          ""
        ],
        "变体速查表（8 条同根条目并入）": [
          "",
          "| 变体 | 报错签名 | 场景 | 根因 | 处理 |",
          "|---|---|---|---|---|",
          "| clone 被重置/卡死 | `fatal: unable to access ... Connection reset by peer` / 超时 | 官方 CLI 安装、源码 clone | 直连不稳，大仓库中断 | gh-proxy codeload tarball；pip 替代安装 |",
          "| 443 连不上 | `Failed to connect to github.com port 443` | 插件 `/plugin install` | 直连 443 被断 | codeload tarball 探路直装；显式代理 |",
          "| SSH 认证失败 | `SSH authentication failed` / `Permission denied (publickey)` | marketplace / 插件克隆 | CLI 默认走 SSH，本机无有效 key | 强制 HTTPS `insteadOf` + 可选 gh-proxy 加速 |",
          "| 公开仓库报权限错误 | `Your account is suspended` / `Please make sure you have the correct access rights and the repository exists` | 网络通但 clone 一律失败 | HTTPS→SSH 全局重写 + 账号/凭据异常 | 匿名 HTTPS 验证 + `--unset-all` 重写规则；gh auth 重新登录 |",
          "| Release 资产下载断连 | TLS 断连 / 下载中断 | deb/AppImage 资产下载 | 资产走 `objects.githubusercontent.com` 不稳 | gh-proxy 前缀 + 下载后校验大小 |",
          "| zip/tarball 下载超时中断 | 下载超时/中断 | 技能/插件 zip 下载 | codeload/raw 链路不稳 | 换通道（tarball ↔ clone 互转）；镜像站离线解压 |",
          "| 失效本地代理残留 | clone 被重置（配置了死代理） | 任何 clone | `http.proxy` 残留指向已挂代理 | `--unset` 清代理 |",
          "| 代理可用但写操作失败（外部 Issue #6689） | `unable to push data to server behind socks5 proxy` | 有代理但 gh push/上传失败 | socks5 代理与 gh 写链路不兼容 | 换 http 代理或对 gh 直连；以 push 成功为准（未本地复现） || 失效本地代理残留 | clone 被重置（配置了死代理） | 任何 clone | `http.proxy` 残留指向已挂代理 | `--unset` 清代理 |",
          "| 工具运行时依赖 GitHub | 构建/更新时访问 GitHub 卡住 | 工具依赖 GitHub 的场景 | 工具少量 GitHub 依赖 | 核心流程离线跑；pip 走清华镜像；gh-proxy |",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 网络层：原命令（clone / 下载 / 安装）完整跑完，无 `reset by peer` / `Timed out`；下载类对照资产页大小一致。",
          "- 配置层：`GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git` 返回 refs；重写规则与失效代理已删。",
          "- 凭据层：公开仓库匿名 HTTPS 可 clone；需要 SSH 的场景 `git ls-remote git@github.com:...` 不再报 `Permission denied`；22 端口被拦时 `ssh -T -p 443 git@ssh.github.com` 能认证成功。- 凭据层：公开仓库匿名 HTTPS 可 clone；需要 SSH 的场景 `git ls-remote git@github.com:...` 不再报 `Permission denied`。",
          "- 安装类：看产物就位（目录/文件），不看 exit 码。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 国内直连 | clone/下载不稳是常态；gh-proxy / codeload / 错峰 / 换网络 |",
          "| 校园网/家宽 | 晚间高峰最差，错峰或代理 |",
          "| 有稳定代理 | 直连即可，gh-proxy 仅备选；记得清掉失效代理残留 |",
          "| Windows/macOS | 同样分层；tarball 方式相同，路径对应各平台用户目录 |",
          "| 完全离线 | clone/下载不可用；本地工具核心流程仍可跑 |",
          ""
        ],
        "症状": [
          "",
          "- `git clone` 卡在 Cloning repository 后报 `Connection reset by peer` / 超时；",
          "- `Failed to connect to github.com port 443`；",
          "- `SSH authentication failed` / `Permission denied (publickey)`；",
          "- 公开仓库也报 `Please make sure you have the correct access rights and the repository exists`；",
          "- Release / zip / tarball 下载反复断连、TLS 中断；",
          "- 部分 CLI 失败时 exit 码仍为 0（坑）。",
          ""
        ],
        "原因": [
          "",
          "国内出口到 GitHub 直连不稳定（Release 资产另走 `objects.githubusercontent.com`，链路更差）；老教程留下的 HTTPS→SSH 全局重写规则与失效代理残留放大问题；SSH key / gh 凭据 / 账号状态异常时，连公开仓库也被拒。",
          ""
        ],
        "解法": [
          "",
          "1. 分层诊断：`curl https://github.com/` + `GIT_CONFIG_GLOBAL=/dev/null git ls-remote` 区分网络层/配置层/凭据层；",
          "2. 网络层换通道：gh-proxy 前缀、codeload tarball、浅克隆、显式代理、错峰/换网络；SSH 被墙时走官方 443 通道（ssh.github.com）；2. 网络层换通道：gh-proxy 前缀、codeload tarball、浅克隆、显式代理、错峰/换网络；",
          "3. 安装类换不依赖 GitHub 的路径：pip 清华镜像、tarball 手工复制；判断成功看产物不看 exit 码；",
          "4. 配置层删重写规则与失效代理；凭据层强制 HTTPS / gh 重新授权；",
          "5. 下载后校验大小/结构。",
          ""
        ],
        "验证证据": [
          "",
          "- 2026-08-29 实测：`GIT_CONFIG_GLOBAL=/dev/null git ls-remote https://github.com/octocat/Hello-World.git` 匿名返回全部 refs；`--unset-all url.git@github.com:.insteadof` 后公开仓库 clone 成功。",
          "- 2026-08-28 实测：CLI clone 被重置（Recv failure: Connection reset by peer），gh-proxy codeload tarball 277KB 秒级完成，24 技能目录就位。",
          "- 2026-08-28 实测：SSH 认证失败 → 强制 HTTPS `insteadOf` 后 `SSH not configured, cloning via HTTPS` + marketplace 添加成功。",
          "- 2026-08-28 实测：codeload tarball 544K / 2.1s 稳定；gh-proxy 7.8s 可用；raw 端点探针超时统一避开。",
          "- 2026-08-28 实测：gh-proxy 下载 13.5MB 秒级，deb 结构确认 exit=0；直连 assets 断连。",
          "- 收录走 PyPI 清华镜像成功；GitHub 直连风险在连通性记录标注。",
          "- 未实测：各镜像加速站的生命周期（有挂/变慢风险，用前先测）；完整企业网络环境（代理白名单等）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「同根（网络层出口不稳，换源家族）」读：",
          "",
          "- [pip 安装超时/慢](pip-mirror-source.html)（网络层同类，PyPI 源配置）",
          "- [npm install 慢/挂起](npm-mirror-registry.html)（网络层同类，npm 源配置）",
          "- [Go 模块拉取慢/超时](go-proxy-cn.html)（网络层同类，Go proxy 配置）",
          "- [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（网络层同类）",
          "- [只有个别网站打不开（其他网站正常）——分层定位](site-open-fail-others-ok.html)（同分层思路：先 curl 定位网络层）",
          "",
          "报错相近但根因不同：[TLS/证书类问题](tls-jdk-upgrade.html)。",
          ""
        ],
        "参考资料": [
          "",
          "- GitHub Docs: Using SSH over the HTTPS port — <https://docs.github.com/en/authentication/troubleshooting-ssh/using-ssh-over-the-https-port>"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/github-direct-connect-fail",
        "agent": "https://trove.xin/agent/problems/github-direct-connect-fail"
      }
    },
    {
      "id": "go-proxy-cn",
      "title": "go get/go mod download 超时（dial tcp ... i/o timeout）→ 换 GOPROXY 国内代理",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "go"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/golang/go/issues/63244",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`go get` / `go mod download` / `go build`（首次拉依赖）报 `go: ...: Get \"https://proxy.golang.org/...\": dial tcp 142.250.69.177:443: i/o timeout`。",
          "",
          "**报错 IP 是 Google 网段**（142.250.x / 142.251.x / 173.194.x）——国内网络直连 Go 默认代理必超时；同一台机器过段时间又可能通，是网络波动，不是代码问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Go 默认模块代理 `proxy.golang.org`（以及校验和 `sum.golang.org`）托管在 Google 基础设施上，国内直连不稳定，TCP 拨号直接 i/o timeout。国内公共代理 `goproxy.cn`（七牛）是官方文档推荐给中国用户的方案，还有 goproxy.io、公司内网 Athens/nexus 等。",
          "",
          "Go 模块下载是「GOPROXY 寻址 + GOSUMDB 校验」两段：GOPROXY 决定从哪个代理拉模块（默认 proxy.golang.org 托管在 Google 基础设施，国内直连 TCP 拨号 i/o timeout）；GOSUMDB 决定从哪个校验和服务器验证模块哈希（默认 sum.golang.org 同样不可直连）。",
          "",
          "只换 GOPROXY 不处理 GOSUMDB，下载仍可能卡在校验和阶段；goproxy.cn 等国内代理通常同时提供 sumdb 代理（`GOSUMDB=sum.golang.google.cn` 或代理自带的 sumdb 服务）。",
          "",
          "确定性推论：`go mod download` 卡住先看卡在哪一段——`GOPROXY` 段（拉模块）还是 `GOSUMDB` 段（验哈希），`go env GOPROXY GOSUMDB` 看当前值；国内环境标准配置 `GOPROXY=https://goproxy.cn,direct GOSUMDB=sum.golang.google.cn`；",
          "",
          "公司内网用 Athens/nexus 时，GOSUMDB 可能要设 `off`（内网代理自带完整性校验，信任链不同）；",
          "",
          "判断路径：报错 URL 是 proxy.golang.org = GOPROXY 问题，是 sum.golang.org = GOSUMDB 问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：临时换 goproxy.cn（direct 兜底回源）",
          "GOPROXY=https://goproxy.cn,direct go mod download",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 临时 — **必做**",
          "",
          "```bash",
          "GOPROXY=https://goproxy.cn,direct go get ...",
          "```",
          "",
          "### 2. 持久化（用户级） — **必做**",
          "",
          "```bash",
          "go env -w GOPROXY=https://goproxy.cn,direct",
          "go env -w GOSUMDB=sum.golang.org",
          "```",
          "",
          "goproxy.cn 可正常转发校验和，国内一般不用关 sumdb。",
          "",
          "### 3. 优先级注意 — **必做**",
          "",
          "环境变量 `GOPROXY=...`（export 在 shell/系统里）> `go env -w` 写入值 > 默认值。",
          "",
          "shell 里 export 了 GOPROXY 时 `go env -w` 不生效（实测警告：does not override conflicting OS environment variable）——改 export 那一处（~/.bashrc / /etc/environment），或显式 `GOPROXY=... go ...`。",
          "",
          "### 4. 校验和也超时 — **必做**",
          "",
          "`GOSUMDB=sum.golang.google.cn`（国内可达镜像），或 `GOSUMDB=off`（牺牲供应链校验，不推荐持久化）。",
          "",
          "### 5. 私有模块 — **必做**",
          "",
          "公司内网模块配 `GOPRIVATE=公司域名`（自动绕过 proxy 与 sumdb）。",
          "",
          "### 6. CI — **必做**",
          "",
          "GitHub Actions 上 `actions/setup-go` 默认 OK，但自建 CI 在国内要显式配 GOPROXY。"
        ],
        "怎么确认修好了": [
          "",
          "拉依赖正常（实测输出形态）：",
          "",
          "```text",
          "GOPROXY=https://goproxy.cn,direct 拉 cobra v1.8.1 + pflag 仅 2.3s（exit 0，实测）",
          "```",
          "",
          "复现基线：`GOPROXY=https://proxy.golang.org,direct` 拉同一版本 → `dial tcp 142.250.69.177:443: i/o timeout`（30s，实测，与 [golang/go#63244](https://github.com/golang/go/issues/63244) 同款）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 校园网/家宽无代理 | 直连 proxy.golang.org 基本必超时 |",
          "| 环境变量冲突 | go env -w 不覆盖 export，改源头 |",
          "| sumdb 超时 | sum.golang.google.cn 或 GOSUMDB=off |",
          "| 私有模块 | GOPRIVATE 绕过 |",
          "| CI | 自建 CI 显式配 GOPROXY |",
          ""
        ],
        "症状": [
          "",
          "`go get` / `go mod download` / `go build`（首次拉依赖）报：",
          "",
          "```",
          "go: github.com/spf13/cobra@v1.8.1: Get \"https://proxy.golang.org/github.com/spf13/cobra/@v/v1.8.1.info\": dial tcp 142.250.69.177:443: i/o timeout",
          "```",
          "",
          "特征：报错 IP 是 Google 网段（142.250.x / 142.251.x / 173.194.x）；",
          "国内网络（校园网/家宽无代理）直连 proxy.golang.org 基本必超时（[golang/go#63244](https://github.com/golang/go/issues/63244)、#34548、#49201 等一批）；",
          "同一台机器过段时间又可能通——网络波动，不是代码问题。",
          "",
          "同族但根因不同（别在本页死磕网络）：",
          "",
          "- `checksum mismatch`（模块校验和不符）→ 代理缓存不同步，`go clean -modcache` 后重新下载（机制核对）；",
          "- `no space left on device`（下载/构建阶段）→ 磁盘满；WSL 下 df 有空间但写不进是宿主盘满，先查 `df -h /mnt/c`。",
          ""
        ],
        "原因": [
          "",
          "Go 默认模块代理 `proxy.golang.org`（以及校验和 `sum.golang.org`）托管在 Google 基础设施上，",
          "国内直连不稳定，TCP 拨号直接 i/o timeout。",
          "国内公共代理 goproxy.cn（七牛）是官方文档推荐给中国用户的方案，还有 goproxy.io、公司内网 Athens/nexus 等。",
          ""
        ],
        "解法": [
          "",
          "1. **临时**：`GOPROXY=https://goproxy.cn,direct go get ...`（`direct` 兜底回源）；",
          "2. **持久化（用户级）**：`go env -w GOPROXY=https://goproxy.cn,direct`（写入 `go env -w` 配置文件）；",
          "   同时建议 `go env -w GOSUMDB=sum.golang.org`（goproxy.cn 可正常转发校验和，国内一般不用关）。",
          "3. **优先级注意**：环境变量 `GOPROXY=...`（export 在 shell/系统里）> `go env -w` 写入值 > 默认值。",
          "   如果 shell 里 export 了 GOPROXY，`go env -w` 不会生效（实测警告：does not override conflicting OS environment variable），",
          "   要么改 export 的那一处（~/.bashrc / /etc/environment），要么显式 `GOPROXY=... go ...`。",
          "4. **校验和**：若 `sum.golang.org` 也超时，可 `GOSUMDB=off`（牺牲供应链校验，不推荐持久化）或 `GOSUMDB=sum.golang.google.cn`（国内可达镜像）。",
          "5. **私有模块**：公司内网模块要配 `GOPRIVATE=公司域名`（自动绕过 proxy 与 sumdb）。",
          "6. **CI**：GitHub Actions 上 `actions/setup-go` 默认 OK，但自建 CI 在国内要显式配 GOPROXY。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，go 1.26.4，校园网无代理）：",
          "  - 基线：本机已配 `GOPROXY=https://goproxy.cn,direct`（环境变量）——goproxy.cn 拉 cobra v1.8.1 + pflag 仅 2.3s（exit 0）；",
          "  - 复现：显式 `GOPROXY=https://proxy.golang.org,direct` 拉同一版本 → `dial tcp 142.250.69.177:443: i/o timeout`（30s），与 [golang/go#63244](https://github.com/golang/go/issues/63244) 同款；",
          "  - 优先级实测：shell 环境变量存在时 `go env -w GOPROXY=...` 报 warning「does not override conflicting OS environment variable」，`go env GOPROXY` 仍是环境变量值；",
          "  - 持久化链路：`go env -w GOPROXY=https://goproxy.cn,direct` 在无环境变量冲突时写入配置（本次因环境变量优先未生效，如实记录）。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「同根（国内换源家族）」读：",
          "",
          "- [pip 安装超时/慢](pip-mirror-source.html)（同根：换源家族，python 侧）",
          "- [npm install 慢/挂起](npm-mirror-registry.html)（同根：换源家族，node 侧）",
          "- [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（同根：换源家族，容器侧）",
          "- [GitHub 直连慢/失败](github-direct-connect-fail.html)（网络层同根：出口不稳）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/go-proxy-cn",
        "agent": "https://trove.xin/agent/problems/go-proxy-cn"
      }
    },
    {
      "id": "herdr-1",
      "title": "`herdr server start` 无效 / 后台 server 未拉起（v0.8.2 没有 server start 子命令）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "herdr"
      ],
      "evidence": "site docs#1;local repro log 2026-08-29;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "装了 herdr，想让它在后台把 server 跑起来——敲 `herdr server start`，结果它不启动任何东西，只把 server 子命令列表（help 文本）打印一遍；`herdr status` 一直是 `server: not running`，`~/.config/herdr/` 下也找不到 `herdr.sock`。看起来像「server 起不来」，其实是 **v0.8.2 的命令面里根本没有 `server start` 这个子命令**——不是坏了，是入口不对。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：herdr 的进程模型是「客户端 + 后台 server（socket 通信）」，TUI 客户端 `herdr` 启动时**按需自动拉起**后台 server；headless 场景才需要手动把 `herdr server` 放后台。它没有 `start`/`stop` 对称的命令面——启动面只有两个入口，停止面只有一个：",
          "",
          "| 面 | 命令 | 说明 |",
          "|---|---|---|",
          "| 交互入口 | `herdr` | TUI 完整客户端，自动拉起后台 server |",
          "| headless 入口 | `herdr server` | 前台运行 server；脚本化时用 `nohup ... &` 放后台 |",
          "| 停止面 | `herdr server stop` | 经 API socket 停止 |",
          "| 确认面 | `herdr status` | `server: running / not running` |",
          "",
          "链路：命令面解析 → server 进程 → socket 写面。断点就在第一步：`server` 是「运行 server」而不是「管理 server」，`start` 子命令不存在，所以敲 `herdr server start` 只会打印帮助，不会报错——**这一类「命令不存在但报错温和」的入口错误，靠报错文本排查会走空**。",
          "",
          "实测（2026-08-29，WSL Ubuntu，隔离 HOME）：headless `herdr server` 启动 → `status` 显示 `server: running` → 四个写面文件就位（socket 权限 `srwx-------`）→ `herdr server stop` → `status` 回到 `not running`，socket 不再可访问。",
          "",
          "推论：判断「server 有没有起来」不看命令敲没敲对，看两个硬信号——`herdr status` 的输出 + `~/.config/herdr/` 下的 socket 写面；脚本里以「status + socket 就位」作为启动成功的判据。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：交互直接用 TUI（后台 server 自动拉起），不要再敲 server start",
          "herdr",
          "# 脚本化（headless）：",
          "nohup herdr server > /tmp/herdr-server.log 2>&1 &",
          "herdr status   # 看到 server: running 即成功",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认命令面 — **必做**",
          "",
          "`herdr --help` 看一眼子命令列表：只有 `herdr`（TUI）与 `herdr server`（headless），没有 `server start`。",
          "",
          "### 2. 交互使用：直接开 TUI — **必做**",
          "",
          "`herdr` 启动 TUI，后台 server 自动拉起；随即 `herdr status` 应显示 `server: running`。",
          "",
          "### 3. 脚本化：headless 放后台 — **按场景**",
          "",
          "```bash",
          "nohup herdr server > /tmp/herdr-server.log 2>&1 &",
          "herdr status",
          "```",
          "",
          "### 4. 停止与复验 — **按场景**",
          "",
          "`herdr server stop` → 再 `herdr status` 确认 `not running`。",
          "",
          "### 5. 写面核对 — **排查**",
          "",
          "`ls -la ~/.config/herdr/`：应出现 `.plugins.lock`、`herdr-client.sock`、`herdr.sock`、`herdr-server.log`；缺 socket = server 没起来。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `herdr status` 输出 `server: running`；",
          "- `~/.config/herdr/` 下 `herdr.sock`、`herdr-client.sock` 就位（权限 `srwx-------`）；",
          "- `herdr server stop` 后 `status` 回到 `not running`，socket 不再可访问。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- 服务器 / CI（无桌面）：用 headless 路径（`nohup herdr server &`），别指望 TUI；",
          "本站实测记录",
          "- 工作区零写入：herdr 只在 `~/.config/herdr/` 与 `~/.local/state/herdr/` 落文件，cwd 不写（2026-08-28 cli-smoke 验证）。",
          ""
        ],
        "症状": [
          "",
          "",
          "输入 `herdr server start` 不启动 server，只打印 server 子命令列表（help 文本）；`herdr status` 显示 `server: not running`；`~/.config/herdr/` 下没有 `herdr.sock`。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "v0.8.2 命令面里**不存在 `server start` 子命令**。正确启动面有两个：",
          "- `herdr`（TUI 完整客户端）——正常使用路径，自动拉起后台 server；",
          "- `herdr server`（headless）——前台运行 server，脚本化时用 `nohup herdr server &` 放后台；",
          "- 停止面 = `herdr server stop`（经 API socket）；确认面 = `herdr status`（server: running / not running）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 交互使用：直接 `herdr` 开 TUI（server 自动拉起），无需显式 start。",
          "2. 脚本化：`nohup herdr server > /tmp/herdr-server.log 2>&1 &`，然后 `herdr status` 确认 running。",
          "3. 停止：`herdr server stop`；再次 `herdr status` 确认 not running。",
          "4. 写面确认：`~/.config/herdr/` 下应出现 `.plugins.lock`、`herdr-client.sock`、`herdr.sock`、`herdr-server.log`。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "本站实测记录（2026-08-29）",
          "- 2026-08-28：`herdr` 完整客户端启动 12s 验证创建 `~/.config/herdr/`（.plugins.lock、herdr-client.log、herdr-server.log、session.json）+ `~/.local/state/herdr/agent-detection/`（19 个 agent 清单 toml，只读情报文件）；工作区（cwd）零写入。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/herdr-1",
        "agent": "https://trove.xin/agent/problems/herdr-1"
      }
    },
    {
      "id": "herdr-2",
      "title": "Windows 上 herdr 用 PowerShell 安装失败（下载被重置；改直下二进制）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "herdr"
      ],
      "evidence": "site docs#2;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "Windows 上按官方说明在 PowerShell 里执行安装命令，下载被重置或脚本执行报错，装不上 herdr。你以为是网络不行，其实有两个叠加因素：**PowerShell 的 Web 下载对 GitHub release 支持差、易被重置**，而且旧安装路径还在流通。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：问题在「下载通道」，不在 herdr 本体。官方安装器 2026-08-22 已修（issue #3131 避免 PowerShell web 下载、#3133 增加本地 cmd installer bootstrap），但 issue #3232（open，2026-08-25）仍有人踩旧路径——修复存在，但**旧命令/旧文档路径还在流通**。",
          "",
          "链路：PowerShell 脚本 → Web 下载（Invoke-WebRequest 类）→ GitHub release → 解压安装。断点通常在第二步：国内出口到 GitHub release 直连不稳，PowerShell 内置下载器对重定向/大文件也不如专业下载工具稳，表现就是「下载被重置 / 脚本报错」。",
          "",
          "实测对照（2026-08-28，WSL Linux）：走 gh-proxy 直下二进制 → `chmod +x` → `--version` 正常（cli-smoke v0.8.2）。**Windows PowerShell 路径本环境未实测**（如实标注）——但机制上同样可以绕开 PowerShell 下载：直下二进制。",
          "",
          "推论：遇到 PowerShell 安装失败，不要反复重试同一条命令；先换通道（浏览器/下载工具/gh-proxy 直下二进制），再验证命令可用。",
          ""
        ],
        "快速修复": [
          "",
          "```text",
          "Windows 推荐路径（机制同 Linux）：",
          "1) 不用 PowerShell 下载——直接从 GitHub release 页面/镜像下载 Windows 对应资产；",
          "2) 国内网络给下载地址加 gh-proxy 前缀加速；",
          "3) 解压得到 herdr.exe 后，herdr --version 验证。",
          "（Linux/WSL 实测可行版本：curl -sL -o herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64 && chmod +x herdr && ./herdr --version）",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是不是「下载层」失败 — **必做**",
          "",
          "症状特征：命令卡在下载/重置，不是 herdr 报错。换浏览器或下载器下同一个文件，能下 = 下载层问题。",
          "",
          "### 2. 直下二进制（绕开 PowerShell 下载） — **必做**",
          "",
          "去 release 页拿 Windows 资产；国内加 gh-proxy 前缀。",
          "",
          "### 3. 升级到含修复的安装器 — **按场景**",
          "",
          "官方 08-22 后的安装器已避免 PowerShell web 下载（#3131/#3133）；如仍用旧命令，先核对官方文档的最新安装方式。",
          "",
          "### 4. 验证 — **必做**",
          "",
          "`herdr --version` 输出 v0.8.2 级版本号即成功。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `herdr --version` 正常输出版本；",
          "- 后续 `herdr` / `herdr server` 命令面可用（见 herdr server 启动条目）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- WSL / Linux：gh-proxy 直下二进制实测可行（2026-08-28）；",
          "- 企业网络（代理）：下载走系统代理；无代理的校园网同样适用 gh-proxy 路径；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "Windows 系统用 PowerShell 执行官方安装命令失败（下载被重置/脚本执行报错）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "PowerShell 的 Web 下载对 GitHub release 支持差/易被重置；官方安装器 08-22 已修（issue #3131 避免 PowerShell web 下载、#3133 增加本地 cmd installer bootstrap），但 issue #3232（open 2026-08-25）仍有人踩旧路径。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 升级到含修复的安装器，或直接下载二进制（`herdr-linux-x86_64` 对应 Windows 资产），不用 PowerShell 下载。",
          "2. 国内网络：二进制走 gh-proxy 前缀加速。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：herdrdev/herdr issue #3232（open，2026-08-25）/ #3131 / #3133（closed 修复安装器），2026-08-28 匿名检索。",
          "- 本环境 WSL Linux，Windows PowerShell 路径**未实测**（如实标注）。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/herdr-2",
        "agent": "https://trove.xin/agent/problems/herdr-2"
      }
    },
    {
      "id": "herdr-3",
      "title": "SSH 远程用 herdr 中文显示乱码/输入无效（终端编码与 locale）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "encoding-format",
      "category_label": "编码与格式",
      "applies_to": [
        "herdr"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "通过 SSH 远程使用 herdr（或在 SSH 会话里跑 TUI）时，中文要么显示成乱码，要么根本输入不进去。你怀疑是 herdr 的 bug，实际上它更可能是**终端链路里某一跳不是 UTF-8**——从远程系统的 locale，到 tmux 类终端的编码开关，都可能吃掉中文。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：终端链路每一跳都可能不是 UTF-8，而 herdr 只是链条末端负责「渲染」的那一环。",
          "",
          "链路：本地终端编码 → SSH（字节流传输，本身不编码）→ 远程 locale（LANG/LC_*）→ 终端复用器（tmux/screen 的 UTF-8 开关）→ 应用（herdr）渲染。",
          "",
          "断点最常见在「远程 locale」：远程系统 `locale` 输出 `C`/`POSIX`（常见于容器和最小化安装）时，宽字符输入解码失败——表现就是乱码或丢输入。其次在终端复用器：tmux 默认按外层终端能力协商，老配置可能关掉 UTF-8。",
          "",
          "实测：本环境无远端会话，**未实测**（如实标注）；WSL 本地实测环境本身是 UTF-8（cli-smoke 会话正常）。",
          "",
          "推论：排查顺序自远及近——先 `locale` 是否全为 UTF-8，再查 tmux 类终端面，最后才是本地终端编码与 herdr 本身；这三步能覆盖绝大多数「SSH 下中文坏了」。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：远程会话里确认/设置 UTF-8 locale，再重开 herdr",
          "locale | grep -i utf-8   # 每个 LANG / LC_* 都应是 *.UTF-8",
          "export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8   # 系统已生成 UTF-8 locale 时立即可用",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认远程 locale — **必做**",
          "",
          "`locale` 输出里 `LANG`/`LC_*` 应含 `UTF-8`（如 `LANG=en_US.UTF-8` / `zh_CN.UTF-8`）；出现 `C`/`POSIX` 即命中。",
          "",
          "### 2. 让 SSH 会话带 UTF-8 环境 — **必做**",
          "",
          "在远端 shell 配置里导出 UTF-8 locale（或通过 SSH `SendEnv`/`AcceptEnv` 传递）；最小化系统先用 `localedef`/发行版工具生成 `zh_CN.UTF-8` 或 `en_US.UTF-8`。",
          "",
          "### 3. tmux/screen 类终端面 — **排查**",
          "",
          "tmux 下确认 UTF-8 已启用（老配置的 `status-utf8` 项；新版本按客户端能力自动协商）。",
          "",
          "### 4. 区分是 herdr 还是终端层 — **排查**",
          "",
          "同一远程会话里 `echo 中文` / `cat` 一个含中文的文件：终端层同样乱 = 不是 herdr 的问题。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 远程会话 `echo 中文测试` 回显正常；",
          "- herdr TUI 内中文字符显示正常、输入不丢字。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- 容器 / 最小化安装：默认 `C` locale 高发，先装 locale 再谈显示；",
          "- tmux 老配置：确认 UTF-8 开关；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "通过 SSH 远程使用 herdr 时，中文显示乱码或输入无效。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "与终端编码/远程 locale 相关（issue #3261 open，2026-08-26）；非 herdr 本体缺陷信号。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 检查远程会话 locale：`locale` 应含 `UTF-8`（如 `LANG=en_US.UTF-8` / `zh_CN.UTF-8`）。",
          "2. SSH 终端编码统一 UTF-8；tmux 类终端面确认 `set -g status-utf8`（如适用）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：issue #3261（open，2026-08-26）匿名检索摘要。",
          "- 本环境无远端会话，**未实测**（如实标注）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/herdr-3",
        "agent": "https://trove.xin/agent/problems/herdr-3"
      }
    },
    {
      "id": "hf-model-download",
      "title": "Hugging Face 模型下载卡住/超时（国内）——HF_ENDPOINT 切 hf-mirror，代码零改动",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-09-01)",
      "source": "本站实测沉淀（2026-09-01）",
      "recorded": "2026-09-01",
      "sections": {
        "你会遇到什么": [
          "",
          "国内跑 AI 项目，`transformers` / `huggingface-cli` 下载模型时卡在 `Fetching xx files` 或一直转圈，最后超时报错（`Could not connect to huggingface.co` 类）。你以为是网速问题，重试 N 次还是一样——因为**问题不在网速，在 huggingface.co 这个域名本身国内不可达**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`huggingface_hub` 的所有下载入口（`hf_hub_download` / `snapshot_download` / `huggingface-cli` / `transformers.from_pretrained`）统一读环境变量 `HF_ENDPOINT` 拼接下载基址。",
          "",
          "这是「改一个变量、全家生效」的机制根因：上层库（transformers/vllm/langchain）内部都调 huggingface_hub，不存在各自独立的下载路径。",
          "",
          "注意新版默认走 Xet 传输协议（`HF_HUB_DISABLE_XET=1` 可关掉退回普通 HTTP），个别网络在 Xet 通道上失败但镜像可通，是第二类误判源。确定性判断路径是「双打点」：`curl -w \"%{http_code} %{time_total}\"` 分别打官方域名与 hf-mirror，000+超时 vs 200 = 直连问题实锤，不用猜。",
          "",
          "Hugging Face 官方域名（`huggingface.co`）未对国内网络做加速，直连在传输层就被丢弃（2026-09-01 实测：`curl https://huggingface.co/` 8 秒超时，code=000）。而国内镜像 `hf-mirror.com` 稳定可达（同实测：200，0.6 秒）。",
          "",
          "**关键机制**：`huggingface_hub` 库的所有下载入口（`hf_hub_download` / `snapshot_download` / `huggingface-cli` / `transformers.from_pretrained`）都认环境变量 `HF_ENDPOINT`——**改一个环境变量，全部代码零改动走镜像**。这是镜像方案的核心价值：不用改代码、不用改依赖。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：切镜像（加到 ~/.bashrc 永久生效）",
          "export HF_ENDPOINT=https://hf-mirror.com",
          "",
          "# 立即验证（下载一个小文件，秒级成功即生效）",
          "huggingface-cli download sshleifer/tiny-gpt2 config.json",
          "# 或 python:",
          "#   from huggingface_hub import hf_hub_download",
          "#   hf_hub_download(repo_id=\"sshleifer/tiny-gpt2\", filename=\"config.json\")",
          "```",
          "",
          "> 如果 `huggingface-cli` 没装：`pip install huggingface_hub`（国内源：`-i https://pypi.tuna.tsinghua.edu.cn/simple`）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认是不是直连问题 — **必做**（一锤定音）",
          "",
          "```bash",
          "curl -s -o /dev/null -w '%{http_code} %{time_total}s",
          "' --max-time 8 https://huggingface.co/",
          "# 000 / 超时 = 直连不可达，走第 2 步",
          "curl -s -o /dev/null -w '%{http_code} %{time_total}s",
          "' --max-time 8 https://hf-mirror.com/",
          "# 200 = 镜像可用",
          "```",
          "",
          "### 2. 设 HF_ENDPOINT 后重试下载 — **必做**（代码不用改）",
          "",
          "还失败，看具体报错：",
          "",
          "- 报证书/代理类错误 → 检查代理环境变量（见 `proxy-env-var` 条目，大小写坑）与 `HF_HUB_DISABLE_XET=1`（新版本默认走 Xet 协议，个别网络要关掉改普通 HTTP 下载）；",
          "- 报 `not found` / 404 → 仓库名或文件名写错，镜像与官方仓库结构一致，直接对官方 URL 核对路径。",
          "",
          "### 3. 缓存位置 — **可跳**",
          "",
          "`HF_HOME`（默认 `~/.cache/huggingface`）控制下载缓存；换机器/清理时删它重下即可，与镜像无关。",
          "",
          "### 4. 离线场景 — **可选**",
          "",
          "下载好的 `HF_HOME` 整个目录拷到内网机器，设同样的 `HF_HOME` 即可离线加载（transformers 优先读本地缓存）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "# 1. 环境变量生效",
          "echo $HF_ENDPOINT        # 应输出 https://hf-mirror.com",
          "# 2. 实际下载成功（耗时可见）",
          "huggingface-cli download sshleifer/tiny-gpt2 config.json",
          "# 输出本地路径即成功；同一文件二次下载走缓存，秒回",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 表现 |",
          "| transformers / langchain / vllm 等上层库 | 内部都调 huggingface_hub，`HF_ENDPOINT` 对它们同样生效——只设一次，全家受益 |",
          "| ModelScope（阿里） | 国内直连即可，不需要镜像；`modelscope download --model Qwen/Qwen2.5-0.5B-Instruct-GGUF` 实测可用（2026-08-30，491MB/58.5s）；双生态项目 HF 侧设 `HF_ENDPOINT`，ModelScope 侧不用动 |",
          "| Docker 容器 | 不继承宿主机变量，需 `docker run -e HF_ENDPOINT=https://hf-mirror.com` 或 compose `environment:` 显式传 |",
          "| Windows | `set HF_ENDPOINT=https://hf-mirror.com`（cmd）或 `$env:HF_ENDPOINT=\"https://hf-mirror.com\"`（PowerShell），行为一致 |",
          ""
        ],
        "症状": [
          "",
          "国内跑 AI 项目的典型形态：",
          "",
          "- `transformers` / `huggingface-cli` / `hf_hub_download` 卡在 `Fetching xx files` 或一直转圈，最终超时（`Could not connect to huggingface.co` 类）；",
          "- 重试 N 次结果一样——不是网速波动，是 `huggingface.co` 这个域名在国内传输层就被丢弃；",
          "- 换机器、换网络（如手机热点）仍然失败；而访问 `hf-mirror.com` 秒开。",
          "",
          "同族但别在本页死磕：报证书/代理类错误 → 检查代理环境变量（大小写坑见 [proxy-env-var](proxy-env-var.html)）与 `HF_HUB_DISABLE_XET=1`；报 `404/not found` → 仓库名/文件名写错，不是网络问题。",
          ""
        ],
        "原因": [
          "",
          "`huggingface_hub` 的所有下载入口（`hf_hub_download` / `snapshot_download` / `huggingface-cli` / `transformers.from_pretrained`）统一读环境变量 `HF_ENDPOINT` 拼接下载基址——这是「改一个变量、全家生效」的机制根因：上层库（transformers/vllm/langchain）内部都调 huggingface_hub，不存在各自独立的下载路径。注意新版默认走 Xet 传输协议（`HF_HUB_DISABLE_XET=1` 可关掉退回普通 HTTP），个别网络在 Xet 通道上失败但镜像可通，是第二类误判源。确定性判断路径是「双打点」：`curl -w \"%{http_code} %{time_total}\"` 分别打官方域名与 hf-mirror，000+超时 vs 200 = 直连问题实锤，不用猜。",
          "",
          "Hugging Face 官方域名（`huggingface.co`）未对国内网络做加速，直连在传输层被丢弃（2026-09-01 实测 8 秒超时 code=000）；国内镜像 `hf-mirror.com` 稳定可达（同实测 200/0.6s）。关键机制：`huggingface_hub` 的所有下载入口（`hf_hub_download` / `snapshot_download` / `huggingface-cli` / `transformers.from_pretrained`）都认环境变量 `HF_ENDPOINT`——**改一个环境变量，代码零改动走镜像**。",
          ""
        ],
        "解法": [
          "",
          "1. **设 `HF_ENDPOINT=https://hf-mirror.com`**（加到 `~/.bashrc` 永久生效，一键命令见「快速修复」），代码不用改。",
          "2. **先一锤定音确认是不是直连问题**（见「快速修复」/「一步步来」第 1 步）：`curl -w \"%{http_code} %{time_total}\"` 分别打官方域名和镜像，000+超时 vs 200 = 直连问题实锤。",
          "3. **还失败按报错分流**：证书/代理类错误 → 查代理变量与 `HF_HUB_DISABLE_XET=1`；404 → 核对仓库路径。",
          "4. **容器显式传**：Docker 不继承宿主变量，`docker run -e HF_ENDPOINT=...` 或 compose `environment:` 补上。",
          "5. **离线场景**：把 `HF_HOME` 整个目录拷到内网机器，设同样的 `HF_HOME` 即可离线加载。",
          ""
        ],
        "验证证据": [
          "",
          "- 2026-09-01 本机实测（WSL Ubuntu 26.04 无代理，huggingface_hub 1.29.0）：`实测复现日志（2026-09-01）`",
          "  - `curl https://huggingface.co/`：8 秒超时 code=000（直连不可达，实锤）；",
          "  - `curl https://hf-mirror.com/`：200 / 0.6s（镜像可用，实锤）；",
          "  - 直连 `hf_hub_download(sshleifer/tiny-gpt2, config.json)`：12 秒挂起被 timeout 杀掉（exit=124，实锤）；",
          "  - `HF_ENDPOINT=https://hf-mirror.com` 后同一调用：1.2 秒成功，662 字节（实锤）；代码零改动。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/hf-model-download",
        "agent": "https://trove.xin/agent/problems/hf-model-download"
      }
    },
    {
      "id": "incident-review-consensus",
      "title": "线上又出事故了——止血、复盘、告警、背锅，on-call 到底该怎么扛？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个我闭着眼都能复述的凌晨：周一 2 点，手机在枕头边炸响。打开告警平台，同一个页面报错触发了 8 条 alert，点进去发现是同一个接口超时了 3 次——凌晨两点把你叫醒的，不是事故，是同一个事故的 8 份复印件。这种事来三次，你就再也不信告警了。然后真正的大故障来了，你的第一反应是\"又是误报吧\"，翻个身继续睡。",
          "",
          "这还不是最可怕的。我见过一个朋友的真实经历：周日晚上，他负责的结算系统被刷走了 2.6 万——87 笔订单重复结算，其中一个人已经跑了，另一个账户里只剩几百块。大领导连夜打电话问他损失，他一夜没睡着。周一早上，代码还没改完，就被拉进会议室开\"批斗会\"：你算什么高级开发、别人犯这种错早滚蛋了。他忍着把修复上线，结果几天后同样的漏洞又出现了——底层分布式锁没锁住，一个线程把另一个线程的锁释放了。",
          "",
          "于是有了第二场批斗会：\"今天必须修完，再出问题自己提离职。",
          "",
          "\"",
          "",
          "还有一种死法，很多人正在经历：复盘写了 30 页 PPT，时间线画得漂漂亮亮，5Why 写得头头是道——然后 3 个月后，同类故障原样再来一遍。不是分析不到位，是产出物不对：大多数团队把复盘停在\"时间线 + 根因分析\"两步，而真正能阻止复发的改进 Action 列表、告警规则更新、SOP，一个都没做。PPT 交上去的那一刻，复盘就已经结束了。",
          "",
          "再补一刀：干了 4 年高强度 on-call 的人会告诉你，故障总在你最不想它来的时候来——团建路上，两个大哥各自写了一个 bug，合流成文件描述符泄漏，凌晨 4 点才挂掉电话，睁着眼睛到天亮；还有一次版本回滚，因为新功能影响回滚稳定性，全网回滚直接让系统趴窝——本来是止血，结果血崩。",
          "",
          "如果你也在经历这些——被假告警骗到失眠、复盘写了没人看、出了事被追责到不敢上报、或者每次上线都怕下一秒手机响——这篇文章是写给同类人的。它不是运维手册，是那些真被事故教育过的人，用睡眠和头发换来的共识：**事故处理从来不是\"找出谁错了\"，是\"让系统活下来，并且永远不要再这样死一次\"。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：事故为什么会一次次重复？四个错位，每个都是\"看着在做事，实际在空转\"。",
          "",
          "**第一，把\"处理事故\"当成\"找根因\"。** 事故现场最没用的两个问题就是\"为什么\"和\"谁的锅\"。你见过急诊室先问病人\"你为什么被砍\"吗？医生先止血，人活下来再问。故障处理也一样：确认根因、确认责任，在快速应急中毫无意义——重点是先把功能恢复。把\"止血\"和\"根因\"绑死在一起，就是灾难：你必须在最紧张的状态下先找到根因，才能决定怎么止血，于是 2 小时能解决的事拖成 6 小时。",
          "",
          "**第二，把\"复盘\"当成\"写报告\"。** 复盘会的唯一目标应该是\"故障永远不再发生\"。但大多数复盘的实际目标是\"把 PPT 交了\"。时间线画了、根因分析了，然后呢？改进项没有负责人、没有截止日期、没有验证方式——\"分析到位但执行没跟上\"，这是复盘的普遍死因。写报告是在安慰自己，拆任务才是真复盘。",
          "",
          "**第三，把\"告警\"当成\"越多越好\"。** 大多数团队的告警系统是\"宁可误报，不可漏报\"的心态搭起来的：告警数量堆上去了，质量没人管。结果就是狼来了——假阳性率超过 30% 的告警规则还留着，比没有还危险：它让你在真正重要的告警来临时，下意识先等等。",
          "",
          "**第四，把\"追责\"当成\"管理\"。** 批斗会式的复盘只会教会团队一件事：如何不背锅。人一旦害怕被追责，就会隐瞒、延迟上报、自己偷偷修——而事故处理最怕的就是\"发现得晚\"。追责文化不是让团队更负责，是让故障藏得更深、炸得更大。",
          "",
          "下面每条经验，都是把事故处理从\"走形式\"拉回\"真闭环\"的做法——它们不是一家之言，是几套独立实践反复验证过的共识。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：事故处理只有三件事——止血、复盘、防复发，顺序不能反",
          "",
          "**处理事故的顺序永远是：先止血（让业务恢复）→ 再复盘（时间线/根因/改进项）→ 最后防复发（告警升级 + SOP + 演练）。** 赶时间只看这一条：下次故障，先别问\"为什么\"，先问\"怎么让它先停下来\"。下面经验 1~5 是为什么和怎么做。",
          "",
          "### 经验 1：止血优先——先找\"变量\"，别追\"根因\"",
          "",
          "现象：第一次独立处理线上故障，我很快就定位到\"不是功能 bug，是运维把版本配置发错了\"，当场松了一口气，觉得事不关己。老板立刻提醒我：排障还没结束，先确认受影响节点范围、确认止血方案。后来我复盘才明白，那一刻我差点犯下新手最典型的错误——把\"找到原因\"当成\"处理完了\"。",
          "",
          "做法：①止血永远是第一优先级，像急诊先止血一样，先让业务活下来，根因后面再查；②止血最快的手段不是查根因，是找\"变量\"——故障一定是某个变化引发的（版本变更、配置改动、机房上下线），而变量比根因好找得多；有团队专门做了\"变更监控中心\"，把系统事件按维度展示，客户一出故障，先把最近所有变更拉出来比对；",
          "",
          "③止血方案也要灰度——我见过一次全网回滚直接把系统搞趴窝的：新功能影响了回滚稳定性，全网回滚时系统血崩。",
          "",
          "所以新功能必须设计成\"可回滚\"，止血动作也要先小范围验证再全量；④指定\"应急一号位\"统筹沟通，其他人同步排查思路、给判断依据，别抢麦、别各查各的。",
          "",
          "为什么：变量好找、根因难找，而止血只需要变量；把止血和根因绑死，意味着必须在高压下做最难的推理，这正是故障处理拖成数小时的原因。可回滚是设计出来的，不是应急时想出来的；回滚都敢全网直上，就是对生产环境没有敬畏。",
          "",
          "### 经验 2：复盘产出 5 样东西，不是 30 页 PPT",
          "",
          "现象：30 页 PPT、时间线精确到分钟、5Why 写了五层——然后同类故障 3 个月后再来一次。为什么？因为复盘产出物不对：90% 的复盘缺失\"改进 Action 列表\"这一环，建议停留在口头，没人负责、没人跟踪、没有验证方式。",
          "",
          "做法：一次有效的复盘，强制产出 5 样东西：①**时间线文档**——只记事实不评价（\"张三失误了\"改成\"执行了配置变更\"），精确到分钟，必须记录\"发现方式\"：是监控发现的，还是用户投诉发现的——差别巨大；②**根因分析（5Why）**——至少追问到第 4 层，直到找到系统性原因；人犯错是必然的，要问\"系统为什么没拦住\"，而不是\"谁的手滑了\"；",
          "",
          "③**改进 Action 列表**——每条有且仅有一个负责人、明确的截止日期、可验证的完成方式；",
          "",
          "不写\"加强培训\"\"提高意识\"这种虚话，必须具体到\"做什么 + 怎么验证\"；④**监控/告警规则更新**——每次复盘都回答一个问题：\"如果告警更好，这次能不能更早发现、甚至避免？\"；⑤**SOP 新建或更新**——下次碰到同类问题，有标准处理流程可循。所有 Action 进项目管理工具跟踪，每周站会 Review 一次逾期项。",
          "",
          "为什么：复盘的唯一目标是\"永远不再发生\"，而\"不再发生\"只能靠落地的 Action 实现，不靠精彩的 PPT。Action 要像功能需求一样严谨：有负责人、有 deadline、有验收——并且要定期演练，确保它在真空期真的管用。如果改进项还没做完故障又来了，还要有临时方案兜底。",
          "",
          "### 经验 3：告警是信号不是噪声——把告警质量当产品做",
          "",
          "现象：真正让你睡不好觉的从来不是告警数量多，是告警质量烂。同一个接口超时 100 次，发 100 条告警；error、warning、info 全走同一条通知路径——凌晨三点手机震了，点开是个 warning。有人实测：把告警分组键改成\"project + event.type + culprit\"后，同一个接口的错误 100 次只出一条告警，每晚告警量直接降 40%。",
          "",
          "做法：①**去重分组**——相同错误归并成一条（Sentry grouping key 精确化，或 Alertmanager 的 group_by + group_interval）；②**分级通知**——P0 立即电话（核心功能报错 + 错误率超阈值 + 用户受影响），P1 工作群通知（非核心报错、性能退化、依赖超时），P2 只记录不通知（重试类、可容忍抖动，周五统一 review）；",
          "",
          "③**路由到人**——按服务归属和 on-call 排班表精确通知当班人，别 @全员——@channel 的结果是每个人都觉得不是自己的事；",
          "",
          "④**反馈闭环**——每次值班结束后，把\"这条告警是真问题还是误报\"反馈回告警系统，让规则学习：假阳性率超过 30% 的规则，要么改条件，要么关掉。",
          "",
          "为什么：告警的本质是\"让你在正确的时间被叫醒\"。留着一个天天误报的告警，比没有还危险——它训练你对告警免疫。判断告警系统好坏的三个数字：过去 7 天告警总数、假阳性率、平均响应时间——这三个数字决定你 on-call 睡得着睡不着。",
          "",
          "### 经验 4：不追责，才能防复发——复盘会不是批斗会",
          "",
          "现象：批斗会式复盘的结局，我在前文那个结算事故里见过两次：第一次被骂，他学会了\"以后犯错被骂，不要解释为什么，只要认错加给解决时间\"；第二次同样的问题复发——不是他不努力，是追责文化让所有人都忙着证明\"不是我的锅\"，没人真正去修系统的漏洞。假复盘结束后，团队成员只学会了\"如何不背锅\"。",
          "",
          "做法：①**对事不对人**——时间线文档只记录事实，不写人名评价；问\"系统为什么没拦住\"，不问\"谁该负责\"；②**制度不靠人**——不要用\"这个人靠不靠谱\"来衡量故障会不会发生，用规则和制度确保系统运行的下限：变更必须生成 diff 并过审批、参数加下限保护、关键参数突变要告警；③**举一反三**——DNS 故障就不能只修出事的那个点，要把生产系统里所有涉及 DNS 的场景全部梳理一遍；",
          "",
          "④**如果你是被追责的那个人**——别把骂往心里去，那是情绪宣泄；",
          "",
          "认错 + 给解决时间，把精力花在真正修好问题上；事情解决才会真正安心，心态调整而不解决问题，下次还会在同一个坑里。",
          "",
          "为什么：人犯错是必然的，系统设计必须默认人会犯错；追责只会教会隐瞒——隐瞒意味着发现得晚，发现得晚意味着损失放大。真正的复盘文化是\"我们都不背锅，我们一起找系统升级的机会\"。",
          "",
          "### 经验 5：平时攒\"应急肌肉\"，出事才不慌",
          "",
          "现象：高强度 on-call 的状态，像开着飞机换引擎——系统在高速运行，变更在持续推进，故障随时可能发生。第一次处理故障的人，十成本事只能发挥八成；而老手镇定自若，不是胆子大，是平时就把\"应急肌肉\"练好了。",
          "",
          "做法：①**熟悉系统**——日常通过画流程图、做代码复盘加深对架构的理解，事故现场你才知道该看哪里；②**沉淀工具脚本**——日志分析脚本、批量排查模板，写一次存好，现场少翻文档、少现写；③**分阶段总结排障方案**——每次故障后把排查步骤沉淀成自己的 SOP，持续迭代；④**开发时就预设灰度、监控、回滚**——可回滚是新功能的设计要求，不是上线后的补救；",
          "",
          "⑤**定期演练**——改进 Action 要像功能一样被测试，故障演练是验证 Action 有没有用的唯一方式；",
          "",
          "⑥**心态**——故障应急不是考试，考试是筛选人，应急是让产品恢复；\"代码和人，只要一个能跑就好\"；就一份工作，身体和家庭比公司重要——出天大的 bug，业务不会凉，写 bug 的人不会死。",
          "",
          "为什么：临场发挥的上限，取决于平时积累的下限。演练的价值在于：改进项还没做完的\"真空期\"，你也有一份临时方案兜底，不会在同一块石头上绊倒两次。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 今晚值班前先看三个数：过去 7 天告警总数、假阳性率、平均响应时间；假阳性率超过 30% 的规则，先改条件或关掉；",
          "2. 下次复盘强制 5 产出物：时间线 / 5Why / Action 列表 / 告警规则更新 / SOP；每条 Action 有唯一负责人 + 截止日期 + 验证方式，进项目工具跟踪，每周站会 Review 逾期项；",
          "3. 变更纪律：新功能必须可回滚；止血方案也要灰度，全量操作前先小范围验证；关键配置变更必须生成 diff 并过审批；",
          "4. 应急一号位：故障会议指定一人统筹，其他人同步排查思路、给判断依据，不抢麦、不各查各的；",
          "5. 被追责时：认错 + 给解决时间，不解释；复盘记录只写事实，不写人名评价。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目**：你没有 on-call 团队——把\"变更前可回滚\"变成肌肉记忆：git tag、镜像打 tag、数据库备份，发布前 30 秒能回退；出事后写 5 行复盘：时间线 / 变量 / 根因 / 下一次怎么防 / 告警怎么补。",
          "- **小团队（5-15 人）**：先落地两件事——告警分级 + 复盘 5 产出物；每周站会花 5 分钟 Review 逾期 Action；别搞追责，时间线只记事实，你会发现报障的人变多了、故障变少了。",
          "- **大型团队**：变更监控中心 + 应急一号位 + 定期演练是标配；告警治理要当产品做（去重 / 分级 / 路由 / 反馈闭环）；复盘 Action 与需求同权，进项目管理工具跟踪，逾期就是欠债。",
          "- **AI 时代**：AI 写代码让变更频率又上了一个台阶——回滚预案和灰度从\"最好有\"变成\"必须有\"；AI 可以帮你起草时间线、分析日志、优化告警规则，但\"负责人、截止日期、验证方式\"必须人定——AI 不会为故障负责，也不会告诉你\"这次变更会引入什么新问题\"；告警和演练在 AI 时代比过去更重要，因为错误传播的速度前所未有。",
          "- **如果你正卡在\"复盘没人看 / 告警天天误报 / 被追责到不敢报\"**：从最小的闭环开始——把最近一次事故的 Action 列表补全（哪怕只有 3 条，每条带上负责人、日期、验证方式）；把假阳性最高的那条告警改条件或关掉；下次事故记录只写事实、不写人名。三个星期后，你会看到差别。",
          "",
          "收尾：我经历过最蠢的事故复盘，是凌晨三点，十个人在会议室互相找责任，而没人问「下次怎么防」。复盘不是批斗会，是给下一次事故买保险。下次线上出事，顺序记牢：先止血，再写五行的复盘（时间线/变量/根因/下一次怎么防/告警怎么补），最后问一句「告警能不能提前十分钟告诉我」。把复盘写成责任状的人会输，把复盘写成保险单的人，会越扛越稳。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/incident-review-consensus",
        "agent": "https://trove.xin/agent/problems/incident-review-consensus"
      }
    },
    {
      "id": "install-channel-freshness-consensus",
      "title": "安装教程会「过期」——链接会死、版本会旧，几十条安装链怎么低成本保鲜？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（行业通识 + 本地实测反思，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-22",
      "sections": {
        "症状": [
          "",
          "我维护着一个工具导航站，每个收录的工具都配一份「怎么装」——命令、下载地址、验证方式，写完还要亲手装一遍才敢上线。很长一段时间里，我以为这样就算负责了。",
          "",
          "直到前段时间的一次例行复查，我才意识到：写的时候亲手验证过，只保证它「当天」是对的。",
          "",
          "那次复查本来应该很平淡。先跑账本扫描——138 条记录（工具 + 组合包 + 问题），0 条超期、0 条即将超期，报告一行 PASS，一切正常。",
          "",
          "然后我犯了那天的第一个错误：我想，「既然要检查，干脆一次全查完」。于是跑了个全量探测——手上全部条目、全部依赖类别、全部端点，一次性并发测过去，然后去泡了杯茶。",
          "",
          "回来的时候，终端没有反应。再开一个：没有反应。连 `echo` 这种级别的命令，等了五分钟都没返回。更糟的是，桌面侧访问那个环境的通道也开始报超时和异常。它就这么整个失去响应了 8 到 10 分钟，最后自己重启恢复——脚本什么结果都没留下。",
          "",
          "最讽刺的是：我在「检查可用性」，结果制造了我自己遇到过的最严重的一次「不可用」。",
          "",
          "环境恢复后我学乖了，改成一条一条、慢慢来：只挑了几条最依赖中转通道的链，用小样本探活重测。这次很顺——4 条链、8 个请求、22 秒，全部通过；其中 3 条走中转加速通道（HTTP 206，分段下载正常），1 条走 GitHub 官方源码域名（HTTP 200）。",
          "",
          "但紧接着是那天的第二个发现：重测通过之后，我顺手对了一遍版本号，然后愣住了——这 4 条链的上游，全部已经发布了新版本。没有一条是「坏」的，但它们全都「旧」了。",
          "",
          "通道还活着，版本已经落伍。而用户不会为「旧」报错——这才是安装内容最阴的腐烂方式。",
          ""
        ],
        "原因": [
          "",
          "先把「一条安装链」说清楚。",
          "",
          "用户按照你的教程装上一个工具，中间其实经过三段：教程文字 → 下载端点（通道）→ 上游版本。三段里，前两段会「死」，第三段会「旧」。",
          "",
          "**为什么通道会死。** 你现在还能顺畅地下载到开源工具，多半不是因为「直连很快」，而是因为中间垫了一层加速——中转代理、镜像站。这是现实：不垫这一层，很多网络环境根本下不动。但这一层不在你的控制里：中转代理是第三方服务，公益的也好、商业的也罢，限流、改域名、停机、被阻断，都是它随时可能发生的变化；镜像同步可能延迟、可能断档；就是你自己的直连，也会随网络环境波动。这些端点的状态，不主动去看，就永远不会知道它什么时候变了。",
          "",
          "**为什么版本会旧。** 上游发版不会通知你。软件迭代的速度天然快于文档更新的速度——你的清单里躺着几十个工具，任何一个都可能在你看不见的地方一个月发好几个版本。旧版本不报错，它只是「还能用」。那个「还能用」是伪装：装上了、跑通了，你完全不知道它是三个月前的东西。",
          "",
          "**为什么人会烂尾。** 手工检查的成本太高了：几十条链，每条都要真实下载、安装、验证，做一次一整天。成本高就会拖延，拖延就会遗忘，遗忘之后，心里那笔「欠着的账」越来越沉，你就更不想碰它。这和仓库越乱越不想整理，是同一套心理机制。",
          "",
          "更隐蔽的是：通道和版本这两个维度，暴露故障的方式完全不同。通道死了，用户会报错，你会被动知道；版本旧了，没有人报错，你只能主动发现。把它们混成一笔账，结果就是显性的那个被检查，隐性的那个永远排在后面。",
          "",
          "所以对策也要反过来设计：把「一次全量硬查」换成「常置的小账本 + 温和的定期检查」——让「该查什么」自动浮出来，每次只动一小批。",
          ""
        ],
        "解法": [
          "",
          "下面每条按「现象 → 做法 → 为什么」展开。文中的数字都来自真实重测记录，没有一个是推算出来的。",
          "",
          "### 经验 0（L1 一句话结论）：通道会死，版本会旧，两笔账分开记",
          "",
          "**把下载通道和上游版本当成两笔独立的账：通道按「多久体检一次」管，版本按「差异对账」管；探活用小样本 + Range，别全量并发硬测。**赶时间只看这一条：每条链记两个字段——通道类型与检查日期、记录版本与核对日期；到期的才测，测完留一行记录。下面是拆开的细节。",
          "",
          "### 经验 1：通道账——最脆的环节是第三方中转，把体检做成 30/90 天分级",
          "",
          "现象：那次温和重测里，4 条链走了两类通道——3 条走中转加速（HTTP 206），1 条走官方直连（HTTP 200），当天全通。但「今天通」不承诺「下个月通」：我的台账里，中转类通道旁边永远挂着一句批注——「当前可达，但可能随时失效」。这不是悲观，是这类服务的真实属性。",
          "",
          "做法：给每条链标注通道类型，只对中转类定更短的体检周期——中转 30 天、直连/镜像 90 天；到期才测，测就用经验 4 的温和方式。发现失败的处理顺序也有讲究：先测备选通道（换一个中转、或者试直连），通道都不可用再改教程正文——多数时候死的是通道，不是工具本身。",
          "",
          "为什么：中转通道的可用性，是第三方的状态，不是你的状态。你能控制的只有「多久看一眼」。30 天是平衡值：坏掉的窗口不会拖太久，检查频率又不至于变成负担。",
          "",
          "### 经验 2：版本账——上游发版从不通知你，把「对账」做成批处理",
          "",
          "现象：就是那次顺手对账发现的：4 条链全部有新版本（herdr v0.8.2 → v0.9.1、oh-my-pi v18.0.11 → v18.2.7、cc-switch v3.20.1 → v3.20.3、superpowers v6.3.0 → v6.4.1）。没有一条坏了，所以没有任何报错、没有任何信号——如果不是主动去对，这个「全部旧了」的状态可以安安静静维持几个月。",
          "",
          "做法：把版本核对做成一个批量动作：集中读一遍各上游的最新版本号，跟台账里的记录版本做差分，只处理「有差异」的条目。跟进要有纪律：下载新版 → 冒烟验证 → 才更新记录——验证之前，宁旧勿假。别因为「新版本号看起来更漂亮」就先把记录改了，那是拿可信度换面子。",
          "",
          "为什么：版本账是隐性账。通道死了会有人告诉你（报错、下载失败），版本旧了没有人会告诉你——它就是安静地旧着，直到某个用户按你的教程装完之后，发现功能对不上。隐性账必须靠「主动对账」清，靠等是等不来的。",
          "",
          "### 经验 3：两笔账分开记——混着记，必有一笔永远排在后面",
          "",
          "现象：最容易犯的错是「测了通道就当体检完成」——下载返回 200，教程标一个「有效」。但通道通只说明「拿得到东西」，不说明「拿到的是现在该给的东西」；反过来，版本最新也不代表装得上——通道一挂，用户连旧版都下不来。两个方向的翻车，我都在真实链条上见过。",
          "",
          "做法：台账里把两个字段分开：通道状态（带检查日期）和记录版本（带核对日期），报告也分列输出。检查节奏分开：通道按 TTL 到期驱动（30/90 天），版本在顺手的时候批量对一次差。",
          "",
          "为什么：两类故障的暴露机制不同——通道是显性故障，版本是隐性故障。混成一笔账的时候，注意力天然被显性故障吸引，隐性故障会永远排在后面。分开记不是形式主义，是让两种节奏各走各的。",
          "",
          "### 经验 4：探活必须温和——小样本、Range 1KB、串行",
          "",
          "现象：就是症状里那次事故的全貌——一个「全量并发探测」跑挂了整个开发环境 8 到 10 分钟：新命令全部挂起、宿主侧调用开始报超时、文件访问同步失效。我没有强杀它，等它自己重启恢复——产出全部丢失。复盘时我意识到最不该的就是「并发」：几十个连接同时打出去，先被压垮的可能不是目标站，而是我自己的网络栈——尤其当开发环境还套着虚拟化层的时候。",
          "",
          "做法：",
          "1. 小样本：一次只测几条链，串行执行，不写并发；",
          "2. Range 探活：用 HTTP Range 请求只取前 1KB（或只要响应头），验证「下得动」而不是「下完整包」；",
          "3. 先算差分再测：让账本先扫出「谁到期了」，只测到期项，不做全量；",
          "4. 出异常先停：探测失败不硬刚、不重试风暴，记录现场，间隔拉长再试。",
          "",
          "为什么：在虚拟化环境里，「并发连接」本身就是有限资源——几十条并发探测，先撞上的往往不是远端限制，而是本机网络栈的上限。而保鲜是「月」级别的低频动作，省下来的那几分钟，完全不值得用整台环境去赌。",
          "",
          "### 经验 5：账本化——让「该查什么」自动浮出来，不靠记忆",
          "",
          "现象：今天我跑了一次例行扫描：138 条记录、0 条超期、0 条 30 天内到期，一行 PASS，收工。全程没有动脑子记任何东西——「现在需要检查吗」这个判断，是账本给我的，不是我想起来的。",
          "",
          "做法：",
          "1. 每条链记四样东西：通道类型（决定体检周期）、通道检查日期、记录版本、版本核对日期；",
          "2. 写一个只读扫描脚本：把每个字段的到期日算出来，输出「超期 / 30 天内到期 / 正常」三档；一条命令，跑完即走；",
          "3. 用「显式触发」而不是定时任务：需要时手动跑（或挂在已有的例行流程里），输出可见、失败可见、可以随时重跑。别把可见性押在定时任务上——它静默失败的时候，你连「没查」都不知道；",
          "4. 每次检查留一行记录：日期、结果、动作（换通道 / 更新版本 / 无变化）。",
          "",
          "为什么：保鲜的本质不是「检查一次」，而是「一直在检查」。任何靠人记住的系统，都会在第三次遗忘时开始腐烂。把「什么时候查什么」交给机制，人只处理「浮出来的问题」——这也是我把这件事从「心理负担」变成「日常动作」的关键。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 盘点：把手上所有安装链列成一张表，一行一条，先填两项——通道类型、记录版本；",
          "2. 定级：中转 30 天、直连/镜像 90 天，写进表里；",
          "3. 扫描：写个几十行的只读脚本（链少就手动算），隔一阵跑一次，只看「到期」清单；",
          "4. 重测：到期项用「小样本 + Range + 串行」验证通道，顺手对一次版本；",
          "5. 记录：把结果写回表；有问题处理问题，没问题留一个日期。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人博客 / 笔记**：链少，不必上脚本，但「通道类型 + 记录版本」两列值得补——每次自己要用到某条安装命令时顺手看一眼版本号，比一年后集中翻修便宜得多；",
          "- **开源项目 README**：安装命令的保鲜天然挂在发布节奏上——每次发版把 README 里的命令跑一遍，这是成本最低的同步点；依赖加速通道的写法尽量给「官方源 + 加速备选」两级，别只写一个；",
          "- **工具站 / 导航站**：条目一多就必须上机制——账本 + TTL 扫描 + 温和重测；「检查」和「内容更新」分开排期：检查是例行的，更新是按结果的；",
          "- **团队内部文档**：把安装链检查挂进已有的例行维护（别新开一个没人看的任务）；中转类通道尤其要写清负责人——静默死掉的通道，第一个受害者是新同事。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/install-channel-freshness-consensus",
        "agent": "https://trove.xin/agent/problems/install-channel-freshness-consensus"
      }
    },
    {
      "id": "java-oom-classify",
      "title": "Java OOM 六种报错签名速查：heap space / Metaspace / StackOverflowError / native thread / GC Overhead",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "jdk"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/mylibs/p/19321491/production-accident-vl01",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Java 服务突然挂掉/502/接口 500，日志里是各种 OOM。不同签名指向完全不同的根因，先对号入座再动手：",
          "",
          "| 报错签名 | 典型根因 | 排查方向 |",
          "|---|---|---|",
          "| `OutOfMemoryError: Java heap space` | 堆被大对象/大集合填满：一次读 200MB 文件进 `byte[]`、无分页查 10w+ 行、ZIP 炸弹内存解压、`-Xmn` 与 `-Xmx` 同值失衡 | 堆 dump + MAT，看谁占内存；检查大文件/大查询/解压 |",
          "| `OutOfMemoryError: Metaspace` | 启动时一次性加载全量 XSD/类（JAXB newSchema 全量初始化），类元数据塞满 Metaspace | 检查启动加载的 schema/class 数量；扩容或分块加载 |",
          "| `StackOverflowError` | 递归无出口：信任第三方参数（传了 -1）导致递归不终止 | 参数校验 + 递归改循环 |",
          "| `OutOfMemoryError: unable to create new native thread` | 线程数超系统上限：无界线程池 + 日志阻塞（磁盘满 + logback AsyncAppender 队列满）→ 线程堆积 | `jstack` 看线程数/栈；磁盘、日志配置、线程池上限 |",
          "| `OutOfMemoryError: GC Overhead limit exceeded` | GC 空转零回收：ThreadLocal 未清理等内存泄漏，Full GC 也回收不了 | MAT 看 Incoming references（如 ThreadLocalMap） |",
          "| 不同位置随机 OOM | JVM 参数失衡：`-Xmn`（年轻代）设成与 `-Xmx` 一样大 | 检查启动脚本 -Xmn/-Xmx 比例 |",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "JVM 内存分堆/元空间/线程栈/本地线程四块，各自独立耗尽：",
          "",
          "| 区域 | 耗尽机制 |",
          "|---|---|",
          "| 堆（Java heap） | 对象持续累积：大文件整体读入、无分页大查询、ZIP 炸弹（极小压缩包解压出 1000 倍以上数据） |",
          "| Metaspace | 类元数据：一次性 newSchema 全量 XSD → 大量类加载 |",
          "| 线程栈 | 递归过深：StackOverflowError |",
          "| 本地线程 | 系统线程数上限（`ulimit -u` / `cat /proc/sys/kernel/threads-max`）：线程池无界 + 业务线程被日志写阻塞（磁盘满 + AsyncAppender 队列满且 discardingThreshold=0）→ 只进不出 |",
          "| GC 空转 | 泄漏对象无法回收：Full GC 回收 <2% 却占用 >98% 时间 → GC Overhead limit |",
          "",
          "JVM 的「内存」不是一块，是四个独立耗尽通道，每个的 OOM 文案和修法都不同：堆（Java heap）由 GC 管理，对象持续累积才耗尽；Metaspace 存类元数据，动态生成类（如全量 XSD→类）才耗尽；线程栈是每线程固定大小（默认 1MB 级），递归过深耗尽（StackOverflowError）；",
          "",
          "本地线程受操作系统线程数上限约束（ulimit -u / threads-max），线程池无界 + 线程只进不出（被日志写阻塞）就耗尽。",
          "",
          "GC 空转（回收 <2% 却占 >98% 时间）是「堆有大量不可达但未回收对象」的指纹。",
          "",
          "确定性推论：OOM 文案里的区域名（Java heap space / Metaspace / unable to create native thread / GC overhead）直接决定排查方向——堆 OOM 查对象累积与泄漏，Metaspace OOM 查类加载数量，native thread OOM 查线程数与阻塞，不要统一「加 -Xmx」；判断路径：先读异常类名再动手。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1. 先开堆 dump（下次 OOM 自动落盘），比瞎猜快",
          "java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/app.hprof -jar app.jar",
          "",
          "# 2. 看 GC 日志确认是哪种耗尽",
          "java -Xlog:gc -jar app.jar   # 或 -verbose:gc",
          "",
          "# 3. 常见立竿见影的改法（按签名对号入座）",
          "# heap space + 大文件下载  -> 改流式输出/外链，不整体读 byte[]",
          "# heap space + 大查询      -> 分页 + 缓存",
          "# 线程耗尽                -> 有界线程池；logback discardingThreshold 调回默认（别设 0）",
          "# 随机位置 OOM            -> 检查 -Xmn，别等于 -Xmx",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 记录签名（先判断是哪一类） — **必做**",
          "",
          "`Java heap space` / `Metaspace` / `StackOverflowError` / `unable to create new native thread` / `GC Overhead limit exceeded` / `Direct buffer memory`——签名不同，武器不同。",
          "",
          "### 2. 复现与取证 — **必做**",
          "",
          "- 开启 `-XX:+HeapDumpOnOutOfMemoryError`，OOM 时拿到 .hprof；",
          "- `jmap -dump:format=b,file=app.hprof <pid>` 手动抓（服务还活着时）；",
          "- MAT/Eclipse MAT 打开 dump，看 Dominator Tree 大对象 + Incoming references（案例八：ThreadLocal 泄漏 = Incoming references 指向 ThreadLocalMap）；",
          "- `jstack <pid>` 看线程栈（native thread 耗尽时看线程总数）。",
          "",
          "### 3. 按根因修 — **必做**",
          "",
          "| 根因 | 修法 |",
          "|---|---|",
          "| 大文件整体读入内存（`readFileContent` 全量 byte[]） | 流式读写（FileInputStream→OutputStream 缓冲循环）或文件挂 FTP/对象存储外链，不经过应用进程 |",
          "| 无分页大查询（10w+ 行反复查） | 限制行数 + 查询缓存 + 缩短轮询周期 |",
          "| ZIP 炸弹（压缩包极小、解压巨大） | 禁嵌套压缩包、校验文件大小与路径、解压到磁盘临时目录并限制总量 |",
          "| Metaspace（全量 XSD 一次性加载） | 临时扩容 `-XX:MaxMetaspaceSize`；根治：服务拆分/分块按需加载 schema |",
          "| 递归无出口（第三方传 -1） | 严格参数校验 + 递归改循环 |",
          "| 无界线程池 + 日志阻塞 | 有界线程池；AsyncAppender `discardingThreshold` 调回合理值（0 = 绝不丢日志，也绝不阻塞保护，磁盘满时线程被拖死） |",
          "| `-Xmn` = `-Xmx` | 恢复合理比例（年轻代约为堆的 1/3~1/2，按业务调） |",
          "| ThreadLocal 泄漏 | 用后 remove（finally 里），避免线程池复用导致引用滞留 |",
          "",
          "### 4. 验证 — **必做**",
          "",
          "压测/复现原触发路径，确认无 OOM；GC 日志平稳；`jstack` 线程数回到正常水位。"
        ],
        "怎么确认修好了": [
          "",
          "同一触发路径下不再 OOM，GC 日志无连续零回收 Full GC，线程数稳定；堆 dump 中不再出现原大对象/泄漏引用。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 现代 JVM（G1/ParallelGC） | 小堆快速分配时 GC Overhead 签名可能直接渲染成 `Java heap space`（本机实测），别只盯字面签名 |",
          "| 未实测 | Metaspace / native thread / 无界线程池场景本机未复现，机制与修法来自原文案例二/四 |",
          ""
        ],
        "症状": [
          "",
          "Java 服务 OOM：`Java heap space`（大文件/大查询/ZIP 炸弹/-Xmn 失衡）、`Metaspace`（全量 schema 加载）、`StackOverflowError`（递归无出口）、`unable to create new native thread`（线程耗尽）、`GC Overhead limit exceeded`（GC 空转）。",
          ""
        ],
        "原因": [
          "",
          "JVM 内存各区域独立耗尽：堆被大对象/大集合填满；Metaspace 被类元数据塞满；线程栈递归过深；本地线程数超系统上限；泄漏对象导致 GC 空转零回收。",
          ""
        ],
        "解法": [
          "",
          "1. 先开 `-XX:+HeapDumpOnOutOfMemoryError` + GC 日志；",
          "2. 按报错签名对号入座（见速查表）；",
          "3. 大文件流式化/外链、大查询分页+缓存、禁 ZIP 炸弹（大小/嵌套/路径校验）、schema 分块加载、参数校验+递归改循环、有界线程池+日志不阻塞、-Xmn 合理比例、ThreadLocal 用后 remove；",
          "4. MAT/jstack 取证后修，压测验证。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制实测（2026-08-30，WSL Ubuntu，OpenJDK 17.0.18）：① `-Xmx64m` 无限分配 → `OutOfMemoryError: Java heap space`；② 无限递归 → `StackOverflowError`（栈深 22691）；③ ZIP 炸弹（200MB 重复字符压缩成 204KB，膨胀比 >1000x）内存解压 → `Java heap space`（对应案例一机制）；④ `-Xmx16m` 慢速泄漏 → 16s 内 31 次 Full GC、14M→14M 零回收（GC 空转机制实测，字面 GC Overhead 签名未稳定复现）。证据：实测复现日志（2026-08-30）。",
          "- 未实测：Metaspace / native thread / 无界线程池 + logback 阻塞场景（无对应业务环境），机制与修法来自原文案例二/四。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/java-oom-classify",
        "agent": "https://trove.xin/agent/problems/java-oom-classify"
      }
    },
    {
      "id": "legacy-code-handoff-consensus",
      "title": "接手一坨\"别人的代码\"——怎么读懂、怎么上手、怎么从里面活下来还变强？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个每个程序员都绕不开的周一。leader 把你带到工位，指着屏幕上某个仓库说：\"这个项目以后你负责，先熟悉一下。\"然后他就走了。你点开仓库：十几万行代码，全局搜索注释，搜出来的结果一只手数得过来；没有 README，没有架构图，没有接口文档；前任已经离职两周，交接文档是一句\"代码里都有\"。你试着把它跑起来，缺三个环境变量、两个中间件、一个你听都没听过的老版本依赖。",
          "",
          "好不容易跑起来了，页面长什么样你还没看清，线上就报了一个 bug，群里有人@你。",
          "",
          "然后你开始了每个接手者都经历过的至暗时刻：一头扎进代码里，从入口一层层往下钻，钻了三天，发现自己连\"这个系统到底是干什么的\"都没搞清楚；某个字段名是拼音缩写，某个判断条件里躺着一个没人敢动的魔法数字，某个看起来\"没用\"的方法，注释写着\"别删，删了会炸\"，你试了，真的炸了；数据库里有几百张表，你连哪张是主表都不知道；",
          "",
          "想问问老人，老人要么忙得没空理你，要么你自己不好意思开口——同一个问题问三遍，你自己都嫌自己烦。",
          "",
          "还有更绝望的版本：你接的不是普通老项目，是\"远古项目\"。技术栈老到像天书，前后端不分离，连启动都成问题；学长留给你的代码没有分支管理，大家各改各的，最后谁也不知道哪份是最新的；或者领导不知道从哪里弄来一个系统和一堆文档，往你桌上一放：\"这个以后你维护。\"你甚至不知道它部署在哪台机器上。",
          "",
          "如果你正在经历这些——对着代码两眼一抹黑、改一个小功能要重新熟悉一遍、问了三次还是没搞懂那个\"反人类设计\"为什么存在——这篇文章是写给同类人的。它不是让你背命令的教程，是那些真接手过烂代码、老项目、远古项目的人，用失眠和踩坑换来的共识：**接手烂代码不是灾难，是职业生涯的必修课；它考验的不是你读代码的速度，是你搞懂业务、沉淀知识、把自己变成\"那个懂的人\"的能力。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：为什么接手别人的代码这么难？五个原因，每一个都指向同一件事——代码不是问题本身，代码外面那层看不见的东西才是。",
          "",
          "**第一，代码只是业务的翻译。** 这是所有接手困境的总根源。一段代码为什么长这样、为什么在这里兜圈子、为什么有个看起来多余的判断——答案不在代码里，在业务里。你懂业务，代码就是大白话；你不懂业务，代码就是天书。而业务知识恰恰是代码里最不会写出来的部分：它散落在需求文档的只言片语、老同事的脑子里、甚至某个已经离职的人的记忆里。",
          "",
          "**第二，业务知识靠口口相传，几乎没人沉淀。** 绝大多数老项目的真实状态是零文档：没有业务文档，没有技术文档，注释约等于零。不是大家懒，是写文档没有激励——需求倒逼、工期紧、快速上线是第一要务，代码跑起来比解释清楚为什么这么写重要得多。于是所有上下文都堆在少数几个老同事的脑子里，他们一离职，知识就跟着走了。",
          "",
          "**第三，烂代码是历代\"临时方案\"的化石层。** 一个维护了五六年的项目，代码里埋着至少三波人的选择：第一波人赶工期留下的 Workaround，第二波人为了不动老逻辑加的硬编码，第三波人看不懂前两波代码、不敢重构只好再包一层。每一层在当时都有充分的理由，但它们叠在一起，就成了\"反人类设计\"和\"故意挖坑\"——没有人故意坑你，只是每一代人都没有义务为你写注释。",
          "",
          "**第四，接手的人默认\"读代码就能懂\"，但代码是结果，不是原因。** 新手最常见的死法是一头扎进代码细节：既然系统长这样，我把每个文件读一遍不就懂了？错。设计意图、权衡取舍、已知的坑，全在代码外面。代码只告诉你\"它做了什么\"，不告诉你\"它为什么这么做\"——而后者才是你改它的时候最需要的东西。",
          "",
          "**第五，烂代码天然没有\"上手文档\"，而接手又往往是硬性任务。** 没有人会为\"接手的可怜鬼\"写一份《本仓库生存指南》，因为写它的人已经走了；而你还不能拒绝——这就是你的工作。所以这件事只能靠你自己：把自己变成文档。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：先跑起来，别急着读代码",
          "",
          "**接手任何代码的第一步都不是读，是让它跑起来、用起来。** 赶时间只看这一条：先当用户，再当开发。下面经验 1~5 是为什么和怎么做。",
          "",
          "### 经验 1：先跑起来，再谈读懂——代码是翻译，产品是原文",
          "",
          "现象：我第一次接手大项目，leader 扔给我一句\"先跑起来，不要读\"，我当时觉得他在敷衍我。结果我硬着头皮读了两天源码，什么都没看懂；后来花半天把环境配好、把系统跑起来、把所有页面点了一遍，再回头看代码，很多\"天书\"突然就通了——因为我终于知道这段代码在实现什么了。",
          "",
          "做法：①**环境搭起来**——接手第一天最重要的产出不是\"读了多少行\"，是\"系统在我机器上跑起来了\"；拿不到环境配置就问，这不是丢人的事，是必要信息；②**当用户过一遍主流程**——亲自用产品，把注册、登录、下单、结算这种主流程走一遍，多问产品经理\"这是干什么的\"；主流程是把握项目主体的最快路径；",
          "",
          "③**先略读文档再读代码**——有文档先花半小时略读，知道系统大概干什么、有哪些功能，这比精读任何一份文档都有用；",
          "",
          "④**梳理外围信息**——数据库地址、中间件、部署机器、日志在哪、怎么打包、怎么发布，这些\"运维信息\"是你后续排障的弹药，越早拿到越好；⑤**别在细节里钻牛角尖**——记住一句话：项目不是一个点，是一个相互关联的面。一开始就往代码细节上钻，越是复杂的项目死得越快——你可能花大量时间层层往下钻，最后对整体功能毫无掌握，迷失在源码里。",
          "",
          "为什么：代码是业务的翻译，你读翻译件之前先读原文，效率高十倍。跑起来是建立\"代码↔行为\"映射最快的方式：看到页面上的一个按钮，再去看它背后的代码，你立刻知道这段代码是干嘛的；反过来从代码推页面，你要推半天还未必对。另外，跑起来还有一个隐藏收益——它帮你验证\"这份代码是不是真的能跑\"，很多老项目的代码根本跑不起来，你越早发现这一点，越早开始修环境，而不是把时间浪费在读一份跑不起来的代码上。",
          "",
          "### 经验 2：先懂业务，再读代码——业务是路标，代码是结果",
          "",
          "现象：接手烂业务代码最难的从来不是语法，是\"这段代码为什么要这样\"。比如一个字段叫 `isOld`，你以为是\"是否老客户\"，读下去才发现是\"是否走旧流程\"；一个方法叫 `getData`，里面藏着三个业务分支，每个分支对应一种你没见过的业务场景。没有业务背景，你连命名都猜不对。",
          "",
          "做法：①**掌握名词**——开发过程中确定的名词（尤其是内部黑话），是阅读代码的钥匙；把每个名词对应的业务含义搞清楚，代码就能和具体功能挂上钩；②**先看数据库表设计**——表结构是业务的骨架，接手时先花时间把表设计看完，不然看代码会不知所云；③**按接口梳理**——功能一般通过 Controller 暴露，如果能每个接口跟着 debug 一遍、把调用流程理清楚，业务就理清了大半；",
          "",
          "别忘了定时任务和消息队列触发的功能，它们不走接口，却是很多老系统的隐藏主线；",
          "",
          "④**问老人**——经常维护此项目的人是最快的捷径，但老人琐事缠身，问之前先自己想清楚、把问题攒一批再问，别拿零碎问题轰炸他；⑤**业务优先于技术**——有些系统的价值在于业务逻辑的复杂，读代码时把\"这个功能解决什么问题\"放在\"这个技术用了什么\"前面。",
          "",
          "为什么：业务是路标，能把零散的代码片段串成线；代码是结果，是业务在特定技术约束下的翻译。翻译件里看不懂的\"为什么\"，九成答案在业务侧。而且业务知识是接手者最缺、最贵的东西——代码可以靠时间堆出来，业务上下文只能靠人讲、靠文档留，一旦老同事走了，它就是绝版资源。你越早把业务搞懂，越早从\"读代码的人\"变成\"懂系统的人\"。",
          "",
          "### 经验 3：知识文档化——读懂的都写下来，这是你给公司和自己的复利",
          "",
          "现象：很多人接手项目\"看过就忘\"：今天理解了某个模块，过几天再看又要重新推一遍；遇到一个坑，踩完了，下次换个人再踩一遍。我见过一个接手老项目的人，花了好几个月把所有代码搞懂之后，做了一件过去几年都没人做的事——把技术文档和业务文档补齐了。之后每个新同事入职，看他的文档，几天就能上手开发。他说这是\"对公司最有价值的部分\"。",
          "",
          "做法：①**读到哪写到哪**——读懂的每个业务都写进文档，包括各种坑；复杂的业务流程画流程图；这个动作不需要等\"全读懂了再写\"，而是边读边写；②**用 A4 纸画模块结构**——读代码时准备一张纸，写下你看到的模块结构，读完再回头看这张纸，你对架构的理解会瞬间立体；③**写文档是思考过程，不是记录过程**——打字比说话慢，而慢会逼你组织语言、发现漏洞：\"这里为什么是这样？",
          "",
          "我好像没搞懂\"，很多问题就是在写的过程中暴露的；④**笔记和测试脚本是一劳永逸的投资**——单元测试既能帮你拆分理解复杂项目，以后出了问题还能用于调试；日志函数、调试脚本同理，写一次存好，作用会随时间越来越大；⑤**给后来人留路**——你受过的苦，别让下一个人再受一遍；你补的文档，就是下一个\"你\"的救命稻草。",
          "",
          "为什么：人脑的默认行为是遗忘，而接手老项目最大的成本就是\"反复重新理解\"。文档化是把一次性理解变成可复用资产的唯一方式——你今天花一小时写清楚的东西，未来会给你省下十个小时，还会给团队省下更多。更重要的是，写文档的过程本身就在帮你理解：说不清楚，就是没想清楚；写不出来的部分，就是你没读懂的部分。",
          "",
          "### 经验 4：带着\"改它\"的目标读——输出导向，从小事做起",
          "",
          "现象：接手代码最常见的两种低效模式：一是目标太宽泛——\"我要熟悉这个项目\"，然后漫无目的地浏览，没有度量，一个月后改个小功能还是要重新熟悉一遍；二是目标太宏大——\"我要把整个项目重构了\"，然后被十几万行代码吓住，迟迟不敢动手。而高效率的接手者都有一个共同点：他们的目标非常具体——\"我要修掉这个 bug\"\"我要加这个功能\"\"我要画完这几张流程图\"。",
          "",
          "做法：①**明确接手目的**——领导让你改显示模块，你的目的就是改显示功能，学习其他模块是手段不是目的；目标明确，做事才有中心、才专注；②**把大目标拆成可输出的小目标**——\"熟悉代码\"无法度量，改成\"一周输出几个模块的流程图、数据流图、时序图、函数关系链\"，或者\"做一次代码分享\"，或者\"修好三个小 bug\"；目标越小越具体，你的步伐越快；",
          "",
          "③**从小事开始**——先读好上手的模块，先解决一个小业务 bug，先加一个小 feature；",
          "",
          "小事的正反馈会拉着你往前走；④**用测试拆解复杂度**——项目难，往往是因为不了解，看着一大堆东西心理上就有压力；通过细致的单元测试把复杂项目拆成小块，每块搞懂一点，心理压力就小一点；⑤**修改代码看输出**——对接口类代码，改一行、看一次输出，是最快的理解方式；给接口调试写个记录日志的函数，非常有用。",
          "",
          "为什么：\"学习是模糊的，实践使你清晰且深刻。\"带着目标读代码，你读的每一行都有目的——它在为你的目标服务，而不是为了\"读完\"；而当你把看到的东西变成自己的输出（流程图、文档、分享、修复）时，这东西就真的属于你了。接手不是阅读理解考试，是实战：你从第一个小任务里学到的，比读一百个文件都多。",
          "",
          "### 经验 5：烂代码是机会，不是惩罚——在烂业务代码里成长",
          "",
          "现象：有人一遇到烂代码就心烦，觉得是公司亏待自己；还有人觉得做业务系统没技术含量，做底层才锻炼技术。但一个接手过大量烂业务代码、最终做到资深的人会告诉你：恰恰相反——接手烂代码，虽然过程更痛苦，但给你更多施展才华的空间、锻炼技术的机会，成长也更多。",
          "",
          "做法：①**换个心态**——烂代码意味着\"这里需要你\"：一团糟的模块等着你梳理，没人敢动的代码等着你理解，没有文档的体系等着你补全；你做的每一件事，都是可见的增量；②**看到业务开发的价值**——偏底层的开发考验细分领域的技术深度，偏业务的开发考验沟通能力、分析解决问题能力、权衡取舍能力、架构能力；业务多种多样、问题千奇百怪，单一领域的经验根本应付不过来，这种广度本身就是竞争力；",
          "",
          "③**把业务熟悉变成壁垒**——对复杂业务系统的熟悉程度，能成为你的竞争壁垒：低级别的晋升靠技术，高级别的晋升靠业务——业务的掌控力比单纯的技术深度更难替代；",
          "",
          "④**评估接手代价，敢于提出来**——当一份代码逻辑不清晰、没有注释、找不到作者，接手的代价（理清逻辑的时间、改代码引入的风险、后续维护成本）可能比重新写一份还高好几倍；合格的工程师要会评估这个代价，必要时把问题摆出来，找最优方案，而不是闷头接下所有烂摊子。",
          "",
          "为什么：烂代码是存量市场，好代码是增量市场——存量市场里你能创造的价值更大，因为\"把乱的理顺\"本身就是稀缺能力。而且业务系统的复杂度是真实的复杂度：它来自真实世界的混乱，比任何刻意设计的算法题都难对付。能在一个烂摊子里把系统理顺、把团队带明白的人，到哪里都是稀缺资源。",
          "",
          "### 经验 6：AI 时代，把 AI 变成你的\"虚拟老员工\"",
          "",
          "现象：以前接手老项目，遇到看不懂的代码只能硬啃或者等老人有空；现在不一样了——我见过一个接手\"远古项目\"的团队，把报错信息直接丢给 AI，让 AI 帮忙排查变量名写错、传输格式不对这种小问题，几秒钟就有答案；还有人让 AI 逐段解释老代码、生成流程图、补注释，效率翻了几倍。",
          "",
          "做法：①**让 AI 当\"翻译\"**——看不懂的代码段直接让 AI 解释：这段在干什么、为什么这么写、有没有隐藏副作用；让 AI 把老代码翻译成你能懂的语言（甚至翻译成你熟悉的语言/框架的等价写法）；②**让 AI 当\"排障助手\"**——接口测试报错、依赖冲突、环境问题，把错误原文丢给 AI，让它给排查步骤；小问题（变量名写错、格式不对）AI 几乎一眼就能看出来；",
          "",
          "③**让 AI 当\"文档写手\"**——读完一个模块，让 AI 帮你整理成文档/流程图/时序图，你审核修正；",
          "",
          "写文档的思考过程不能省，但打字的工作可以外包；④**让 AI 当\"重构顾问\"**——先让 AI 分析代码结构、找重复、提重构建议，再结合你对业务的理解决定改不改；⑤**但业务上下文，AI 给不了你**——AI 能解释代码\"做了什么\"，解释不了\"为什么业务要这么做\"；这部分还是要靠问老人、看文档、用产品——AI 是你的加速器，不是你的业务导师。",
          "",
          "为什么：接手老代码的瓶颈从来不是\"看不懂语法\"，是\"不懂上下文\"。AI 恰好把前者解决了——语法、结构、逻辑、报错，这些是 AI 的强项；而后者（业务为什么这样设计）恰恰是 AI 最弱的地方，也是你真正要花功夫的地方。把 AI 能干的交给 AI，把 AI 干不了的留给自己，这就是 AI 时代接手老项目的正确姿势。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. **第一天**：把资料要全（代码地址、数据库地址、中间件地址、部署机器、登录账号、需求文档、接口文档），然后唯一的 KPI 是\"让它在本地跑起来\"；",
          "2. **第一周**：当用户把主流程点一遍；找老人问一次\"这个系统是干什么的\"（先攒好问题再问）；看完数据库表设计；梳理一遍接口清单；",
          "3. **每天**：读懂的写进自己的笔记/文档，画一张流程图；遇到坑记下来，别让它二次发生；",
          "4. **两周内**：定一个具体可输出的目标——修好三个小 bug，或者画完一个模块的时序图，或者做一次代码分享；",
          "5. **全程警惕**：别一头扎进代码细节；别急于求成；别不好意思问；别把\"看过了\"当\"懂了\"。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **学生/毕设接手学长项目**：第一件事是版本管理——把源码推到 gitee/github，建分支（一个解读分支、一个改错分支），别让\"谁也不知道最新代码在哪\"继续恶化；技术栈太老、前后端不分离，可以评估改造成自己熟悉的结构（先套用熟悉项目的结构再按需求改）；接口用 swagger 测，测试前确认数据库有对应数据；扩展功能分宽度（加新功能）和深度（把旧功能做智能），一个人负责一个模块。",
          "- **小团队**：把\"问老人\"变成制度——交接时强制产出交接文档（环境怎么配、怎么部署、日志在哪、已知的坑）；接手的人每周同步一次进度；文档是团队的资产，不是个人的功德。",
          "- **大型团队**：文档落地机制 + 代码浏览工具（Source Insight / VS Code 的函数跳转）是标配；立项之初就要建立文档机制，记录项目的前世今生；老项目安排\"代码考古\"专项，把最混乱的模块逐个梳理成图。",
          "- **AI 时代**：把 AI 当\"虚拟老员工\"——解释代码、排查报错、生成文档、提重构建议；但业务上下文必须人来沉淀：你问老人得来的、自己用产品体验出来的，才是 AI 给不了你的部分，把它写进文档，就是给未来所有接手者（包括未来的你）存的钱。",
          "- **如果你正卡在\"看不懂、不敢改、没人问\"**：从最小的闭环开始——今天先让项目跑起来；明天当用户点一遍主流程；这周画一张你最常改的模块的流程图。接手烂代码不是短跑，是马拉松：你可以慢，但不能停。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/legacy-code-handoff-consensus",
        "agent": "https://trove.xin/agent/problems/legacy-code-handoff-consensus"
      }
    },
    {
      "id": "llm-training-loss-spike",
      "title": "训练时 loss 突然尖峰（spike）/ 数值发散（NaN、inf）怎么办",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "dev-debug",
      "category_label": "编程与调试",
      "applies_to": [
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/GlenTt/p/22742530",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "训练（大模型微调/从头训练）跑到一半，loss 曲线突然跳出尖峰：可能单步跳高后自己回落，可能连续多个 spike，也可能 spike 之后永远回不到原来的水平，甚至直接变 `NaN` / `inf`。网上最常见的建议是\"降学习率\"，但现象不等于原因——先分清是数据、超参还是链路问题，再动手。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "一次参数更新 = 学习率 × 梯度。loss 尖峰本质是**更新量失控**，但同一现象背后有三类常见根因：",
          "",
          "1. **数据**：异常 batch（损坏样本、序列过长、label 非法）——通常表现为单步 spike 后恢复；",
          "2. **超参/链路**：学习率过大、缺梯度裁剪、AMP 精度问题——通常表现为连续 spike 或永久不恢复；",
          "3. **目标/实现**：loss mask、label、多卡归约错误——通常表现为\"loss 平滑但验证不改善\"或只有多卡异常。",
          "",
          "按尖峰类型分流（原文诊断表 + 机制实测）：",
          "",
          "| 现象 | 先看什么 | 最小验证 | 确认后才处理 |",
          "|---|---|---|---|",
          "| 单步 spike 后恢复 | batch 上下文（token/序列长度/来源/label）、grad norm | 重放相同 batch | 修复损坏数据；合法难样本不丢弃 |",
          "| 连续多个 spike | grad norm、clip 系数、相对更新量、学习率 | 对齐这些指标 | 调整已确认的超参/结构问题 |",
          "| spike 后永久不恢复 | 相对更新量、optimizer state | 健康 checkpoint 对照 | 修复根因后从健康 checkpoint 恢复 |",
          "| 第一步 NaN | 输入、activation、loss | 单 batch FP32 重放 | 修复第一个非有限运算或精度配置 |",
          "",
          "训练循环里，参数更新的本质是 `参数 += 学习率 × 梯度`，loss 尖峰就是一次（或连续几次）更新量过大。更新量由两个因子相乘：学习率（超参，恒定或按 schedule 变化）和梯度（由当前 batch 的损失反向传播算出）。",
          "",
          "两个因子各自爆炸都会让乘积失控：学习率过大 = 每次更新都太大，即使梯度正常也会发散；梯度异常 = batch 里有损坏样本/序列过长/label 非法时，这一批算出的梯度方向或量级异常，单步更新就巨大。",
          "",
          "这就是“现象 ≠ 原因”的结构：尖峰是结果，要从“哪个因子失控”反推根因。两个排障工具对应两个因子：**重放相同 batch**（如果同一批数据稳定复现尖峰，说明是数据因子；不能复现，说明是随机性/并行/状态因子）；**看 grad norm**（裁剪前梯度范数持续抬升 = 梯度因子风险；grad norm 正常但更新仍大 = 学习率因子）。",
          "",
          "梯度裁剪兜底的机制也由此而来：它直接把梯度范数限制在阈值内，等于给“梯度因子”上了保险丝，即使数据或计算偶发异常，单步更新量也被钳制在安全范围。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1. 兜底：梯度裁剪（PyTorch 标准 API，max_norm=1.0 是常见起点）",
          "torch.nn.utils.clip_grad_norm_(model.parameters, max_norm=1.0)",
          "",
          "# 2. 从最后一个健康 checkpoint 恢复 + 学习率降到原来的 1/3~1/10",
          "#    （恢复前用异常 batch 和普通 batch 各重放一次，确认根因）",
          "```",
          "",
          "机制实测：学习率过大 → loss 在约 100 步内从 0.05 级爆炸到 `inf`（不恢复）；同样学习率加梯度裁剪 `max_norm=1.0` → 全程有限稳定；注入单步尖峰后从健康 checkpoint 降学习率恢复 → 正常收敛。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先分类：什么类型的 spike — **必做**",
          "",
          "单步后回落 / 连续多个 / 永久不恢复 / 直接 NaN。类型决定排查方向（见上表）。",
          "",
          "### 2. 重放相同 batch — **必做**",
          "",
          "- 能稳定复现 → 数据问题优先（损坏样本、label、mask）；",
          "- 不能复现 → 检查随机算子、并行执行、前一状态差异。",
          "",
          "### 3. 看 grad norm，而不是只看 loss — **必做**",
          "",
          "裁剪前 grad norm 持续抬升 → 更新链路风险；用**相对更新量**（参数更新幅度/参数幅值）判断训练是否还在有效学习。",
          "",
          "### 4. 按根因选处理手段 — **必做**",
          "",
          "- 数据损坏 → 修数据；合法难样本 → 不丢弃；",
          "- 更新量失控 → 梯度裁剪 / 降学习率（一次只改一个变量，做单变量对照）；",
          "- 已 NaN / 不恢复 → 从健康 checkpoint 恢复，不要只修模型参数后继续用可疑 optimizer state。",
          "",
          "### 5. 防再犯：pilot run — **必做**",
          "",
          "正式训练前小规模试跑：覆盖完整 warmup、峰值学习率一段、代表性数据、至少一次 validation、固定随机种子。监控分三档：每步（loss/梯度是否有限）、窗口（趋势偏离）、周期（验证指标）。"
        ],
        "怎么确认修好了": [
          "",
          "loss 恢复下降趋势且不再出尖峰；grad norm 回到健康区间；从健康 checkpoint 恢复后训练能持续推进；validation 指标同步改善。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 单卡正常、多卡异常 | 先查归约与控制流（有效 token 数、loss 归约、各 rank 是否一致 backward），不先调超参 |",
          "| FP16/AMP | 先 unscale 再统计和裁剪梯度（GradScaler 判断思路见原文） |",
          "| 框架差异 | 梯度裁剪各框架都有对应实现（PyTorch clip_grad_norm_ / TF clip_by_global_norm 等），机制相同 |",
          "| 未实测 | 本条目为等价最小机制复现（numpy 2 层 MLP），未在完整 LLM 训练框架复现；LLaMA-Factory 微调相关见 combo-local-llm-finetune |",
          ""
        ],
        "症状": [
          "",
          "loss 曲线突然尖峰：单步 spike 后恢复、连续 spike、spike 后永久不恢复，或直接 `NaN`/`inf`；grad norm 同步异常抬升。",
          ""
        ],
        "原因": [
          "",
          "更新量 = 学习率 × 梯度，尖峰本质是更新量失控。根因分三类：数据（异常 batch）、超参/链路（学习率过大、缺梯度裁剪、AMP 精度）、目标/实现（mask、label、多卡归约错误）。现象 ≠ 原因，先证据后处理。",
          ""
        ],
        "解法": [
          "",
          "1. 按尖峰类型定位（单步/连续/不恢复/NaN）；",
          "2. 重放相同 batch + 看 grad norm / 相对更新量；",
          "3. 按根因处理：修数据 / 梯度裁剪 / 降学习率 / 从健康 checkpoint 恢复（一次只改一个变量）；",
          "4. 训练前跑 pilot run + 分档监控防再犯。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-08-30，Python 3.14 + numpy 2.5.2，venv+清华镜像）：2 层 MLP 回归 y=sin(x) 机制复现——lr=0.01 收敛（loss 0.065→0.026）；lr=1.0 发散（25 步 loss 5.4e51，98 步 inf，不恢复）；lr=1.0+clip=1.0 全程有限（gradnorm 裁剪后=1.0）；注入单步尖峰（loss 0.048→0.224）后从健康 checkpoint 降 lr 恢复收敛（0.028）。证据：实测复现日志（2026-08-30）。",
          "- 原文：石头开会《模型训练稳定性：从训练链路到排障手册》（2026-08-28，博客园）。",
          "- 未实测：完整 LLM 训练框架（PyTorch/LLaMA-Factory）上的实况复现（环境重，标注\"机制已复现、环境未复现\"，SM-076）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/llm-training-loss-spike",
        "agent": "https://trove.xin/agent/problems/llm-training-loss-spike"
      }
    },
    {
      "id": "local-llm-deploy-consensus",
      "title": "本地部署大模型，装好了却\"用不起来\"——从\"能跑\"到\"好用\"到底差在哪？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "我交过最贵的一笔\"模型学费\"，是一张白租两周的 8 卡 A100。",
          "",
          "当时我们做智能客服，想法很简单粗暴：上最大的模型，效果肯定最好。于是我们花两周时间搞来一台 8 卡 A100 的机器，跑 72B 参数的模型。结果呢？推理延迟 6-8 秒一条回复，用户等得想骂人。更扎心的是：72B 模型在我们客服场景下，跟 7B 模型比，准确率只高了不到 2 个百分点——但推理成本高了十倍不止。",
          "",
          "后来我们换成 7B + LoRA 微调，延迟降到 2 秒以内，准确率反而比没微调的 72B 高了一截（88% → 93.7%）。",
          "",
          "那张 A100 的钱，白烧了。",
          "",
          "这不是我一个人的故事。另一条线是\"装了像没装\"：你明明照着教程一步步装，最后却卡在\"连不上\"或者\"能用但没人用\"。我见过太多团队卡在这个状态——模型跑起来了，demo 演示很酷，一上生产就翻车：高峰期一来就宕机、回答慢到没人用、业务准确率低到没法上线。有个说法特别准：**绝大多数团队的开源模型落地，都卡在\"能用\"到\"好用\"之间。**",
          "",
          "还有更隐蔽的一类坑：不是装不上，是\"装上之后悄悄变傻\"。同一个显卡、一模一样的权重，推理软件栈的微小差异就能让模型在关键位置输出完全不同的 token，甚至让工具调用彻底失败。你以为是模型不行，其实是环境在骗你。",
          "",
          "如果你也在经历这些——本地部署装好了但没人用；或者正准备部署，满脑子\"越大越好\"；或者 demo 跑通了、一上生产就露馅——这篇文章是写给同类人的。它不是安装教程，是那些真把开源模型落进生产的人，用算力账单和线上事故换来的共识：**本地部署大模型，难点从来不是\"跑通\"，是\"在有限的资源下，跑得准、跑得快、跑得稳、跑得久\"。**",
          ""
        ],
        "原因": [
          "",
          "先拆解\"从能跑到好用\"到底差在哪。我把见到的翻车方式归成四类，每一类都有机制原因，不是某个人不努力：",
          "",
          "**第一类：选型迷信\"参数越大越好\"。** 这是最常见的坑。很多人认为模型参数越大效果越好，盲目部署几十亿、上百亿参数的模型，完全忽略自己的服务器配置和业务并发需求。结果是服务卡顿、推理超时、显存溢出。真相是：模型选型不是\"谁参数大选谁\"，是\"谁合适选谁\"——你的业务是轻量文本分类，1B-3B 的模型可能比 72B 更合适，因为准确率差不到 2%，成本差 10 倍。",
          "",
          "**第二类：把\"部署\"当成\"落地\"。** 很多团队以为 AI 落地就是下载模型、简单部署、直接调用，忽略了量化、微调、提示词、推理引擎优化、业务适配这些核心工程环节。通用开源模型是基于海量公开数据训练的，对你公司内部的业务术语、工单规范、行业规则完全陌生——原生权重直接上业务，准确率往往不足 60%。\"能跑\"和\"能用\"之间，隔着一整条调优流水线。",
          "",
          "**第三类：把\"demo 跑通\"当成\"生产可用\"。** 只实现基础调用功能，不做批量推理优化、异常重试、资源限流、模型热更新这些工程处理，面对业务高峰期的突发流量，轻则响应超时，重则服务宕机。demo 环境一个人点两下没问题，生产环境 20 个人同时问就是另一回事。",
          "",
          "**第四类：把\"环境\"当成\"理所当然\"。** 本地部署最大的隐性杀手不是模型，是环境：依赖包版本、推理引擎、量化后端、CUDA 版本，任何一个微小差异都可能让模型行为大变。有研究指出，同一个模型光是推理软件栈不同，输出就可能完全不同——这就是为什么\"明明一样，我的就变傻\"。",
          "",
          "还有一个藏在所有死法下面的共同点：**缺少业务导向的验收。** 用通用 benchmark 选型、用\"能出结果\"验收、用\"demo 演示\"判断好坏——没有业务评估集，就没有人能在翻车前发现问题。下面每条经验，都是把这些坑一个个填上的做法，来自多个团队的实测，不是纸面理论。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：先定约束，再选模型——拒绝\"大参数执念\"",
          "",
          "**选型不是\"越大越好\"，是\"先定约束再选模型\"：先想清楚业务场景、硬件预算、并发要求、延迟红线，再倒推模型规模。** 赶时间只看这一条：轻量文本分类、内容解析、简单问答，优先 1B-3B 轻量模型；高并发复杂推理再考虑 7B 及以上；压测必须用业务真实数据。下面经验 1~5 是为什么和怎么做。",
          "",
          "### 经验 1：量化是必做项——但量化不是无脑压缩",
          "",
          "现象：原生 FP16 模型显存占用高、推理速度慢，扛不住并发。很多人知道要量化，但要么不量化（显存爆了），要么无脑 4-bit（精度崩了）。我见过团队量化后准确率断崖下跌，直接怀疑模型不行——其实是量化配置不对。",
          "",
          "做法：用 4-bit 量化（nf4 + double quant）作为轻量业务场景的默认起点，量化后用业务评估集验证准确率损失可接受。一组实测参考：原生 FP16 单卡显存 3.2G、单条推理 2.1s；4-bit 量化后显存降到 800M 左右、延迟压到 400ms 以内，准确率几乎不降。关键原则：**量化不是\"压得越狠越好\"，是\"在准确率可接受的范围内压到最小\"——每换一种量化配置，都要用业务数据复测。**",
          "",
          "为什么：量化是本地部署性价比最高的一步——它直接决定你能不能\"装得下、跑得动\"。显存减到 1/4、延迟减到 1/5，代价是几乎不损失的准确率，这笔账怎么算都划算。但\"几乎不损失\"是有前提的：不同量化位宽、不同后端（GGUF/AWQ/GPTQ）、不同 compute dtype，效果差异很大——这就是为什么必须复测。",
          "",
          "### 经验 2：部署只是开始——微调 + 提示词才是\"业务适配\"",
          "",
          "现象：\"重部署、轻调优\"是第二个高频坑：直接用开源模型原生权重，不做任何场景微调与提示词优化。结果原生输出对业务术语完全陌生，准确率低到没法上线。有团队实测：原生权重做运维日志分析，准确率只有 58%——接近抛硬币。",
          "",
          "做法：三步缺一不可——①小样本微调（LoRA 即可）：用几百到几千条真实业务数据做指令微调，让模型学会你的术语和格式；②提示词工程：用系统提示词统一输出规范（固定格式、禁止冗余、温度调低）；③量化 + 微调 + 提示词组合使用。同一团队做完这三步，准确率从 58% 提升到 92%。参考线：**先微调 + 提示词把准确率拉到业务可接受线，再谈优化延迟和并发。**",
          "",
          "为什么：通用模型的知识是\"公开世界的知识\"，不是\"你们公司的知识\"。微调是让模型\"懂你的业务\"，提示词是让模型\"按你的规矩说话\"——前者解决会不会，后者解决像不像。跳过这两步，量化做得再好，也只是\"把一辆不懂路况的车开得更快\"。",
          "",
          "### 经验 3：评估用业务真实数据——别信通用 benchmark",
          "",
          "现象：选型翻车的第二个原因：用通用 benchmark 比模型。通用 benchmark 上 72B 的分数全面优于 7B，但你的客服场景里 72B 的优势可能只有 2%。这种差距在 benchmark 报告里看不出来，只有在真实业务评估集里才会暴露。我们那张白租两周的 A100，就是 benchmark 思维交的学费。",
          "",
          "做法：选型时建一个\"业务评估集\"：从真实业务里抽 50-200 条代表性输入（含边界情况和刁钻 case），让候选模型用同一套提示词跑一遍，人工或按关键词覆盖率打分，同时记录延迟和成本。用\"业务准确率 × 延迟 × 成本\"三维对比，而不是只看 benchmark 分数。评估集要固化下来，每次换模型、换量化、换提示词都重跑——它就是你的回归测试。",
          "",
          "为什么：benchmark 测的是\"模型的公共能力\"，你要的是\"模型在你业务上的表现\"——两者相关性远没有你以为的高。业务评估集把选型从\"看广告\"变成\"看疗效\"，而且它一旦建好，就是长期资产：以后每次模型升级、每次调优，都有据可依。",
          "",
          "### 经验 4：工程优化优先于算法调优——\"跑通\"≠\"能上线\"",
          "",
          "现象：\"重功能、轻稳定性\"是第三个高频坑。只实现基础调用，不做批量推理优化、异常重试、资源限流、模型热更新，高峰期一来就宕机。另一个极端是团队执着于把准确率从 92% 提到 93%，却不管单卡只能扛 2 个并发——生产环境里，稳定可用、低延迟、高并发，远比极致准确率重要。",
          "",
          "做法：上生产前把工程四件套补齐——①并发/批处理：用 vLLM 等推理引擎做 continuous batching，别一条条串行调；②限流与降级：给推理服务设并发上限和队列，超限返回降级结果而不是雪崩；③重试与超时：调用侧设超时和重试，区分\"模型慢\"和\"模型挂了\"；④模型热更新：版本切换不停服。参考线：**先保证\"高峰扛得住、故障不雪崩\"，再回头抠准确率。**",
          "",
          "为什么：模型服务的失败模式和普通 Web 服务不一样——它不是\"超时\"就是\"OOM\"，而且每个请求都贵。工程四件套的成本远低于一次线上事故的代价。记住那句话：demo 是\"能跑\"，生产是\"跑得稳\"——中间隔着的是工程，不是模型。",
          "",
          "### 经验 5：环境即地狱——\"报错在撒谎\"，软件栈要锁版本",
          "",
          "现象：本地部署最隐蔽的坑是环境。同一个模型、同一张显卡、一模一样的权重，推理软件栈的微小差异（量化后端、注意力实现、KV 缓存量化方式、CUDA/驱动版本）就能让输出完全不同，甚至工具调用彻底失败。新手遇到这种情况，第一反应是\"模型坏了\"——其实\"报错在撒谎\"，真正的问题在环境。",
          "",
          "做法：三件事——①环境版本锁死：把 CUDA、驱动、推理引擎、Python 依赖的精确版本写进部署脚本和文档，不要\"最新版\"；②最小可复现：出问题先怀疑环境，用官方推荐的 Docker 镜像或最小依赖集复现，排除软件栈差异；③变更可追溯：任何环境变更（升级推理引擎、换量化后端）后，必须重跑业务评估集——环境变了，行为就可能变。",
          "",
          "为什么：大模型的输出是软件栈的\"函数\"——同一个权重在不同栈上不是同一个模型。环境问题最难排查，因为它\"看起来\"是模型问题。锁版本 + 最小复现 + 变更重测，是把环境从\"薛定谔的坑\"变成\"可控的变量\"的唯一办法。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 先定约束再选型：业务场景、硬件预算、并发要求、延迟红线写下来，再倒推模型规模（轻量场景 1B-3B 起步）；",
          "2. 建业务评估集：50-200 条真实业务输入（含边界 case），固化下来当回归测试；",
          "3. 量化 + 微调 + 提示词三步走：4-bit 量化打底，LoRA 微调适配业务，系统提示词统一输出；",
          "4. 工程四件套：并发批处理、限流降级、重试超时、热更新，一个都不能少；",
          "5. 锁环境版本：CUDA/驱动/推理引擎/依赖精确版本写进部署脚本，变更后重跑评估集。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人尝鲜（自己电脑）**：没有业务压力，重点是别被\"越大越好\"带偏——先问自己到底要做什么：聊天？代码？文档问答？轻量任务用 1B-8B 的 GGUF 量化模型就够了；把评估集做成\"几个固定问题\"，换模型后重跑一遍，比看 benchmark 有用得多。",
          "- **小团队试点（1-3 个业务场景）**：选一个业务价值最高、边界最清晰的场景做试点；先跑通\"量化 + 微调 + 提示词 + 评估集\"这条最小链路，再谈铺开；试点阶段最该守住的指标是\"业务准确率达标\"而不是\"模型参数大\"。",
          "- **生产系统 / 高并发**：工程四件套全上，评估集进 CI；模型版本、环境版本、提示词版本全部纳入版本管理；每次升级都走\"评估集 → 灰度 → 全量\"，禁止直接换模型。",
          "- **AI 时代（用 AI 部署 AI）**：让 AI 帮你写部署脚本、调量化参数、生成评估集，但\"验收\"必须是人的——AI 会告诉你\"装好了\"，不会告诉你\"装好的这个和你上次那个输出不一样\"。环境版本锁和业务评估集，是 AI 时代本地部署的最后一道人工护栏。",
          "- **如果你正在\"装了像没装\"的状态**：先别怀疑自己，按顺序排查——环境版本（锁死）→ 评估集（建起来）→ 量化微调提示词（按业务适配）→ 工程四件套（扛并发）。绝大多数\"用不起来\"，不是你不会装，是只装了\"能跑\"的那一半。",
          "",
          "收尾：我劝过最多人的一句话是：别上来就 70B。本地大模型的真相是，九成场景 8B 量化就够用，瓶颈从来不在参数，在约束、数据和环境。我自己最惨的一次折腾：模型换来换去，最后发现问题是 Python 环境被我不小心升级了，模型根本没换对地方。先锁环境、再谈模型——这一句话，值你一整晚的觉。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/local-llm-deploy-consensus",
        "agent": "https://trove.xin/agent/problems/local-llm-deploy-consensus"
      }
    },
    {
      "id": "logfire-1",
      "title": "FastAPI+SQLModel 测试报 sqlalchemy.exc.NoInspectionAvailable",
      "domain": "observability",
      "domain_label": "可观测性",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "logfire"
      ],
      "evidence": "site docs#1",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "简单 FastAPI + SQLModel 应用，加了 `logfire.instrument_pydantic` 后用 TestClient 测试，报 `sqlalchemy.exc.NoInspectionAvailable: No inspection system is available for object of type ...`；**去掉该行则正常**。",
          "",
          "你会以为 logfire 坏了——其实是老版本 logfire 与 SQLModel 的已知冲突，早已修复。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "logfire 的 Pydantic 插桩会检查模型对象，与 SQLModel 的 SQLAlchemy 检查路径冲突（SQLModel 自身已知问题，[fastapi/sqlmodel#71](https://github.com/fastapi/sqlmodel/issues/71)）；logfire 2.11.0 及更早版本触发，**2024-12-30 于 2.11.1 修复**（官方 issue #728，closed）。",
          "",
          "“插桩（instrumentation）”是**在第三方库里挂钩子**：logfire 的 Pydantic 插桩会拦截模型对象的创建/校验，往里面注入观测逻辑。",
          "",
          "冲突的根源是**两条检查路径撞车**：SQLModel 的模型同时是 Pydantic 模型和 SQLAlchemy 模型，logfire 按 Pydantic 路径检查它，SQLModel 底层却走 SQLAlchemy 的检查路径（inspection system），两边对同一个对象各查各的、机制互不兼容，旧版 logfire 触发时就炸出 NoInspectionAvailable。",
          "",
          "这是**依赖版本组合**问题（logfire × SQLModel 的特定版本交叉），不是代码写错——“去掉 instrument_pydantic 就正常”恰恰证明你的业务代码没问题。",
          "",
          "处理这类问题的确定性路径固定：先查“这个报错是不是已知问题”（issue 搜索，官方修复版本号）→ 升级到修复版本 → 用最小用例复测（本市场就是这么验证的：按 issue 描述写最小用例，4.41.0 下 HTTP 200 无报错）。",
          "",
          "版本组合类问题，升级优于改代码。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：升级 logfire 到 >=2.11.1（本市场收录 4.41.0 已包含修复）",
          "pip install -U logfire -i https://pypi.tuna.tsinghua.edu.cn/simple",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 升级 logfire — **必做**",
          "",
          "```bash",
          "pip install -U logfire",
          "```",
          "",
          "国内加 `-i https://pypi.tuna.tsinghua.edu.cn/simple`。",
          "",
          "### 2. 仍出现时：确认插桩调用位置 — **必做**",
          "",
          "`instrument_pydantic` 与 `instrument_fastapi(app)` 的调用位置要对（4.41.0 的 FastAPI 插桩必须在 app 创建后传参调用，且需先装 `logfire[fastapi]` extra）。"
        ],
        "怎么确认修好了": [
          "",
          "最小用例运行正常（实测输出形态）：",
          "",
          "```text",
          "FastAPI + SQLModel + TestClient + instrument_pydantic + instrument_fastapi",
          "→ HTTP 200，无 NoInspectionAvailable（logfire 4.41.0 实测）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| logfire <2.11.1 | 必现冲突（issue #728） |",
          "| logfire ≥2.11.1 | 已修复（官方 alexmojaki 确认） |",
          "| 调用位置 | FastAPI 插桩在 app 创建后传参调用 |",
          "| extra | 需先装 `logfire[fastapi]` |",
          "| 未实测项 | 真实遥测链路（需项目 token） |",
          ""
        ],
        "症状": [
          "",
          "",
          "简单 FastAPI + SQLModel 应用，加了 `logfire.instrument_pydantic` 后用 TestClient 测试，报 `sqlalchemy.exc.NoInspectionAvailable: No inspection system is available for object of type ...`；去掉该行则正常。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "logfire 的 Pydantic 插桩会检查模型对象，与 SQLModel 的 SQLAlchemy 检查路径冲突（SQLModel 自身已知问题，[fastapi/sqlmodel#71](https://github.com/fastapi/sqlmodel/issues/71)）；logfire 2.11.0 及更早版本触发，2024-12-30 于 2.11.1 修复（官方 issue #728，closed）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 升级 logfire 到 >=2.11.1（本市场收录 4.41.0 已包含修复）：`pip install -U logfire`（国内加 `-i https://pypi.tuna.tsinghua.edu.cn/simple`）。",
          "2. 仍出现时：确认 `instrument_pydantic` 与 `instrument_fastapi(app)` 的调用位置（4.41.0 的 FastAPI 插桩必须在 app 创建后传参调用，且需先装 `logfire[fastapi]` extra）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 官方 issue #728（closed 2024-12-27）：维护者 alexmojaki 评论「Fix released in 2.11.1」（匿名 API 补证，）。",
          "- 本市场实测（2026-08-28，隔离 venv，Python 3.14.4）：按 issue 描述写最小用例（FastAPI + SQLModel + TestClient + instrument_pydantic + instrument_fastapi），logfire 4.41.0 下运行 HTTP 200、无 NoInspectionAvailable。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/logfire-1",
        "agent": "https://trove.xin/agent/problems/logfire-1"
      }
    },
    {
      "id": "logfire-2",
      "title": "升级后刷 Invalid type NoneType for attribute 'http.port' value 警告",
      "domain": "observability",
      "domain_label": "可观测性",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "logfire"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "logfire 升级后（案例为 2.11.0 → 2.11.1）控制台大量出现 `Invalid type NoneType for attribute 'http.port' value` 警告；**不配置 logfire 时不出现**。你以为是 logfire 的 bug——其实根因不在 logfire，它只是让第三方问题浮出水面。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "**根因不在 logfire**：第三方 HTTP 客户端（案例为某 kiota 系 httpx 适配器）在 `url.port` 为 None 时仍把 `http.port` 设置为 None，OpenTelemetry 属性校验对 NoneType 报 warning；logfire 只是把该警告暴露出来（官方 issue #749，closed，维护者定位结论）。",
          "",
          "可观测性链路是“**生产方 → 校验方 → 展示方**”三段：你的应用代码（通过第三方 HTTP 客户端）产生 span 和属性 → OpenTelemetry SDK 按规范校验属性类型 → logfire 把结果展示到控制台。这个警告链路的根因在**生产方**（第三方客户端传了 None 值），校验方只是按规范报 warning，展示方（logfire）只是把 warning 显示出来。",
          "",
          "“升级 logfire 后出现警告”的错觉来自时间巧合：新版本把原本忽略的告警显示出来了，但制造问题的代码一直是第三方。",
          "",
          "判断归属有固定方法：**看警告在链路哪一段被制造**——logfire 侧没有可配置项能消除它（说明它只是展示），第三方修复后警告自动消失（说明根因在第三方）。处理这类“展示层背锅”问题，先定位生产方，别在展示方打补丁。",
          "",
          "| 角色 | 行为 |",
          "|---|---|",
          "| 第三方 HTTP 客户端 | url.port 为 None 时仍设置 http.port=None |",
          "| OpenTelemetry 属性校验 | 对 NoneType 报 warning |",
          "| logfire | 让警告浮出水面（不是根因） |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：定位调用链里设置 http.port 的第三方客户端，升级或等其修复",
          "pip list | grep -iE \"kiota|httpx\"",
          "```",
          "",
          "确认是第三方问题后，该 warning 无害，可忽略。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 定位设置 http.port 的第三方 HTTP 客户端 — **排查**",
          "",
          "kiota 系/httpx 适配器类——升级或等待其修复。",
          "",
          "### 2. 确认是第三方问题后，该 warning 无害，可忽略 — **必做**",
          "",
          "不要在 logfire 侧绕过（无配置项需求）。"
        ],
        "怎么确认修好了": [
          "",
          "第三方修复后警告消失（预期形态）：",
          "",
          "```text",
          "升级第三方客户端 → 控制台不再刷 Invalid type NoneType for attribute 'http.port'",
          "```",
          "",
          "或确认根因后忽略：警告无害，不影响功能（官方 issue #749 维护者结论）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 第三方客户端 | kiota 系 httpx 适配器（issue #749 案例） |",
          "| 修复位置 | 第三方应在 url.port 非 None 时才设置 http.port |",
          "| logfire 侧 | 无配置项需求，不要绕过 |",
          "| 未实测项 | 真实遥测链路未实测（需项目 token）；本市场 4.41.0 命令面/最小复现未触发 |",
          ""
        ],
        "症状": [
          "",
          "",
          "logfire 升级后（案例为 2.11.0 → 2.11.1）控制台大量出现 `Invalid type NoneType for attribute 'http.port' value` 警告；不配置 logfire 时不出现。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "根因不在 logfire：第三方 HTTP 客户端（案例为某 kiota 系 httpx 适配器）在 url.port 为 None 时仍把 http.port 设置为 None，OpenTelemetry 属性校验对 NoneType 报 warning；logfire 只是让该警告浮出水面（官方 issue #749，closed，维护者定位结论）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 定位调用链中设置 http.port 的第三方 HTTP 客户端（kiota 系/httpx 适配器类），升级或等待其修复。",
          "2. 确认是第三方问题后，该 warning 无害，可忽略；不要在 logfire 侧绕过（无配置项需求）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 官方 issue #749（closed 2024-12-30）：维护者定位到第三方 httpx 适配器，指出 http.port 只应在 url.port 非 None 时设置（匿名 API 补证，）。",
          "- 本市场实测：logfire 4.41.0 命令面/最小复现未触发该 warning；如实标注「真实遥测链路未实测（需项目 token）」。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/logfire-2",
        "agent": "https://trove.xin/agent/problems/logfire-2"
      }
    },
    {
      "id": "minio-to-rustfs-migration",
      "title": "MinIO 社区版停更归档，Docker 部署的文件服务怎么办（迁移 RustFS 数据零丢失）",
      "domain": "deploy",
      "domain_label": "构建与部署",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "minio",
        "rustfs",
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/haoxianrui/p/22673027",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "MinIO 社区版 2025-12 官宣进入维护模式、2026-02 GitHub 仓库归档只读：不再有提交，已披露 CVE 不再修复，官方资源全部转向商业版 AIStor（订阅制）。还在用社区版跑文件服务（比如 Docker 部署的对象存储）等于抱着一个不再更新的黑盒。",
          "",
          "另一个典型触发是：升级客户端后上传报 `XAmzContentSHA256Mismatch`——不是配置错了，是新版 AWS SDK 默认走流式分块签名，旧版 MinIO 解析不了，而且永远不会修。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- **停更是事实**：MinIO 官方把社区版冻结，安全修复\"视情况评估\"，仓库 README 标注 \"THIS REPOSITORY IS NO LONGER MAINTAINED\"。",
          "- **签名不兼容是停更的伴生伤**：新版 AWS SDK（v2）默认流式分块签名，旧 MinIO 只支持经典签名，报 XAmzContentSHA256Mismatch 且官方永不修复。",
          "- **为什么能零丢失迁移**：MinIO 磁盘上不是\"一个对象一个文件\"，而是对象同名目录（xl.meta 元数据 + 分片）。RustFS 自 1.0.0-alpha.89 起直接兼容该磁盘格式——整目录 `cp -a` 即可，数据不过网络，旧目录原样保留就是天然回退备份。",
          "",
          "> 实测环境：Docker 29.1.3（WSL2 Ubuntu），minio/minio RELEASE.2025-09-07，rustfs/rustfs latest，2026-08-30。镜像经 docker.m.daocloud.io 加速拉取。",
          "",
          "停更的真相在「生态契约」而不在代码本身：MinIO 社区版冻结后，上游（AWS SDK v2）继续演进，新版 SDK 默认流式分块签名而旧 MinIO 只支持经典签名——不是 MinIO「坏了」，是它不再跟随上游契约变化，签名握手必然失败且官方永不修复。",
          "",
          "迁移零丢失的机制在磁盘格式：MinIO 不是「一个对象一个文件」，而是对象同名目录（xl.meta 元数据 + 分片）；RustFS 直接兼容该磁盘格式意味着 `cp -a` 整目录搬迁 = 元数据与分片原样保留，数据不过网络。",
          "",
          "确定性推论：判断 S3 兼容服务能否迁移，看「磁盘格式兼容」而非「API 兼容」（API 兼容可以随时断，磁盘格式兼容才能离线搬迁）；旧目录原样保留就是天然回退备份——迁移后先验证读写，验证不过直接切回；判断路径：报 XAmzContentSHA256Mismatch = 签名代际不匹配，不是网络或配置问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1) 记录旧 MinIO 数据目录（本文示例 /tmp/trove-minio-test/data）",
          "docker inspect minio --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}'",
          "# 2) 停旧容器（数据目录原样保留）",
          "docker stop minio && docker rm minio",
          "# 3) 整目录复制 + 属主对齐（RustFS 以 UID 10001 非 root 运行，必须 chown）",
          "cp -a /tmp/trove-minio-test/data /tmp/trove-minio-test/data-rustfs",
          "chown -R 10001:10001 /tmp/trove-minio-test/data-rustfs",
          "# 4) 起 RustFS，端口约定与 MinIO 完全一致（9000 API / 9001 控制台）",
          "docker run -d --name rustfs -v /tmp/trove-minio-test/data-rustfs:/data \\",
          "  -e RUSTFS_ACCESS_KEY=rustfs-admin -e RUSTFS_SECRET_KEY='换成强密码' \\",
          "  -p 9000:9000 -p 9001:9001 rustfs/rustfs:latest",
          "# 5) 验证旧数据直接可见",
          "docker logs rustfs | tail",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 停用旧 MinIO，但先记下数据目录 — **必做**",
          "",
          "`docker inspect` 的 Mounts 输出里记下 Source（宿主机路径）和 Destination（容器内路径，MinIO 一般是 /data）。旧容器删掉没关系，数据在宿主机目录里。",
          "",
          "### 2. 整目录复制（不是挑文件） — **必做**",
          "",
          "`ls /data/public` 看到的不是图片文件，而是一堆和对象同名的目录（里面是 xl.meta 和分片），所以\"挑对象复制\"行不通。直接 `cp -a` 整个数据目录到平级新目录，旧目录原样保留 = 回退备份。",
          "",
          "### 3. chown 10001 再启动（最容易崩的一步） — **必做**",
          "",
          "MinIO 官方镜像以 root 运行，挂哪都能写；RustFS 镜像 Dockerfile 声明 `USER 10001`，以非 root 身份运行，而 bind mount 不转换权限——容器内 UID 10001 写 /data 时按宿主机目录属主检查。不 chown 的话容器能起但写不进去。别用 chmod 777（等于向服务器所有用户开放写权限）。",
          "",
          "### 4. 启动 RustFS 并验证 — **必做**",
          "",
          "- `docker ps` 状态 Up；`docker logs rustfs` 无报错。",
          "- 控制台 http://服务器IP:9001 用启动时 AK/SK 登录，应直接看到旧 MinIO 的桶和文件。",
          "- API 验证（S3 兼容）：`list_buckets` 能看到旧桶、旧对象可读、新对象可写。",
          "",
          "### 5. 客户端同步换（别忘） — **必做**",
          "",
          "旧 MinIO 的 Service Account 不会迁移：应用侧统一换 RustFS 启动时的新 AK/SK。Java/Go/Python SDK 只需改 endpoint + 凭据；IP 直连时 AWS SDK 需开 path-style 寻址（`pathStyleAccessEnabled(true)`），否则虚拟主机式寻址把桶名拼进域名会 DNS 解析失败。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "# S3 API 验证：旧桶可见 + 旧对象可读 + 新对象可写（实测输出）",
          "buckets after migration: ['legacy-bucket']",
          "legacy object readable: b'hello from minio legacy'",
          "new object roundtrip: b'written on rustfs after migration'",
          "```",
          "",
          "浏览器控制台能看到旧桶旧文件 = 迁移端到端完成。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 选型边界 | SeaweedFS 最成熟但 Master/Volume/Filer 多角色架构要重新学；Garage 适合地理分布式；Ceph 适合 PB 级。单机自用、中小规模、迁移成本最低选 RustFS |",
          "| 生产规模 | 本次实测为单机 Docker 模拟存量 MinIO → RustFS 全链路；多节点纠删码、生产并发未测；PB 级核心存储选 SeaweedFS/Ceph 更稳 |",
          "| 没换客户端 | 只换服务端不换 SDK，旧 MinIO Java SDK 仍能连 RustFS（S3 协议）；建议顺手换 AWS SDK 标准实现，以后不绑任何一家 |",
          "| 停更警示 | 对象存储选型不是一锤子买卖；S3 协议这层抽象才是值钱的——服务端换成谁都是配置级改动 |",
          "| 客户端 SDK | Java/Go/Python 只需改 endpoint + AK/SK；IP 直连必须开 path-style（虚拟主机式寻址会 DNS 解析失败） |",
          ""
        ],
        "症状": [
          "",
          "MinIO 社区版仓库归档只读 / README 标注不再维护；升级 AWS SDK 后上传报 `XAmzContentSHA256Mismatch`；Docker 部署的对象存储想迁移但怕丢数据。",
          ""
        ],
        "原因": [
          "",
          "MinIO 官方资源转向商业版 AIStor，社区版 CVE 不再修复；新版 AWS SDK 默认流式分块签名而旧 MinIO 不支持（永不修复）；RustFS 兼容 MinIO 磁盘格式（≥1.0.0-alpha.89），整目录复制即可零网络迁移。",
          ""
        ],
        "解法": [
          "",
          "`cp -a` 整目录复制旧 MinIO 数据 → `chown -R 10001:10001` → `docker run` 起 RustFS 挂载新目录 → 应用侧换新 AK/SK（IP 直连开 path-style）→ 控制台/API 验证旧桶旧对象 + 新写入。",
          ""
        ],
        "验证证据": [
          "",
          "实测复现日志（2026-08-30）（2026-08-30 本地 Docker 全链路实测：建桶上传 → 停旧 → cp -a + chown → RustFS 起服 → 旧桶/旧对象直接可见 + 新对象 roundtrip 通过）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/minio-to-rustfs-migration",
        "agent": "https://trove.xin/agent/problems/minio-to-rustfs-migration"
      }
    },
    {
      "id": "mysql-binlog-flashback",
      "title": "MySQL 误删数据后如何恢复（binlog 闪回）",
      "domain": "deploy",
      "domain_label": "构建与部署",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "mysql"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/zrui-xyu/p/22563082",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "手滑执行了 `DELETE FROM <表>` 没带 WHERE（或 WHERE 条件写错），数据瞬间全没了；表里可能还插着生产数据，不能直接回滚，也不知道从哪恢复。误删后最忌讳的是继续写入或重启数据库——binlog 里可能还有救，乱操作会让数据彻底丢失。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "MySQL/MariaDB 开启了 binlog（`log_bin=ON`，`binlog_format=ROW`）时，每次 DML 都会按事务记录到 binlog 文件。误删的 DELETE 也是一个普通事务，它**之前**的所有写入（INSERT/UPDATE）都还在 binlog 里。只要在误删事务的起始 position 处截断重放，就能把数据恢复到误删前一刻。",
          "",
          "binlog 是 MySQL 的**顺序追加日志**：所有 DML 按提交顺序写成事件流，每个事务在文件里有确定的起始 position。误删的 DELETE 只是这个事件流里的一个普通事务——它之前的所有写入（INSERT/UPDATE）都还在。flashback 的本质是「把 binlog 当成可回放的时间线」：定位误删事务的起点，重放它之前的事件，数据就回到误删前一刻。",
          "",
          "可行性前提：`binlog_format=ROW`（row 格式才含完整前后镜像，statement 格式无法精确重建）；`log_bin=ON`（没开就没有时间线）。",
          "",
          "确定性推论：能不能闪回先查两个开关（`SHOW VARIABLES LIKE 'binlog_format'` / `log_bin`），ROW + ON 才可行；恢复要在**独立实例**上重放（binlog 重放会执行写操作，别在生产库直接跑）；定位 position 靠 `mysqlbinlog` 按时间/关键字找误删语句；",
          "",
          "判断路径：报错「无 binlog」= 环境没开，闪回不可用，只能走备份。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 1. 定位误删事务的起点（记下 DELETE 那行的 Pos）",
          "SHOW BINLOG EVENTS IN \"mysql-bin.000001\";",
          "",
          "# 2. 导出误删前所有写入（stop-position = DELETE 起点）",
          "mariadb-binlog --start-position=<INSERT起点> --stop-position=<DELETE起点> /var/log/mysql/mysql-bin.000001 > flashback.sql",
          "",
          "# 3. 恢复到新库（rewrite-db 换库名，先验证再上生产）",
          "mariadb-binlog --start-position=<INSERT起点> --stop-position=<DELETE起点> --rewrite-db=\"坏库->恢复库\" /var/log/mysql/mysql-bin.000001 | mysql",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 紧急止损（第一步最重要） — **必做**",
          "",
          "```bash",
          "FLUSH TABLES WITH READ LOCK;   # 锁库禁止写入",
          "cp -r /var/log/mysql/ /backup/ # 备份 binlog（防止被清理）",
          "```",
          "",
          "**绝对不要重启 MySQL**——内存里未落盘的变更可能丢失；也不要继续写数据，新的写入会污染恢复点。",
          "",
          "### 2. 确认 binlog 开启并列出日志 — **必做**",
          "",
          "```bash",
          "SHOW VARIABLES LIKE \"log_bin\";  # 必须 ON，否则只能靠备份",
          "SHOW BINARY LOGS;               # 找到最新的 binlog 文件",
          "```",
          "",
          "### 3. 用 SHOW BINLOG EVENTS 定位误删事务 — **排查**",
          "",
          "```bash",
          "SHOW BINLOG EVENTS IN \"mysql-bin.000001\";",
          "```",
          "",
          "关键看两行：INSERT 事务的 `Annotate_rows`（数据写入）和 DELETE 事务的 `Annotate_rows`（误删）。DELETE 事务的 **Pos 列就是恢复截止点**。",
          "",
          "### 4. 闪回导出 + 重放（先恢复到新库验证） — **必做**",
          "",
          "```bash",
          "# 导出误删前区间（含 INSERT 不含 DELETE）",
          "mariadb-binlog --start-position=<INSERT事务Pos> --stop-position=<DELETE事务Pos> /var/log/mysql/mysql-bin.000001 > flashback.sql",
          "",
          "# 建恢复库 + 重放（rewrite-db 避免覆盖坏库）",
          "mariadb-binlog --start-position=<INSERT事务Pos> --stop-position=<DELETE事务Pos> --rewrite-db=\"原库->恢复库\" /var/log/mysql/mysql-bin.000001 | mysql",
          "",
          "# 验证行数与内容一致后，再决定切回生产",
          "SELECT COUNT(*) FROM 恢复库.表;",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "SELECT * FROM 恢复库.表;",
          "```",
          "",
          "行数与误删前一致（本机实测：5 行全删后恢复 5 行，内容/顺序完全一致），且闪回 SQL 里 `grep DELETE` 为 0（没有把误删动作重放进去）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| MySQL 8.x | 命令为 mysqlbinlog，机制相同；binlog 默认开启 |",
          "| MariaDB 11.8 | 命令为 mariadb-binlog（mysqlbinlog 为兼容别名）；本机实测环境 |",
          "| DROP TABLE/库 | 需要全量备份 + 备份点之后的 binlog 重放，闪回只覆盖 DML |",
          "| binlog 被清理 | 只能靠物理备份，覆盖部分丢失 |",
          "| 未实测项 | DROP 场景全量恢复、binlog2sql 反向 SQL、超长 binlog 性能 |",
          ""
        ],
        "症状": [
          "",
          "手滑 DELETE 不带 WHERE 全表被删（或 UPDATE 误改全表）；数据在 binlog 开启的库中仍有痕迹；重启数据库或继续写入会丢失恢复机会。",
          ""
        ],
        "原因": [
          "",
          "binlog（ROW 格式）按事务记录全部 DML；误删 DELETE 是独立事务，其起始 position 之前的写入仍完整保存在 binlog。用 `mariadb-binlog/mysqlbinlog --start-position --stop-position` 精确导出误删前区间并重放，即可恢复到误删前一刻；`--rewrite-db` 可先恢复到新库验证。",
          ""
        ],
        "解法": [
          "",
          "1. 止损：`FLUSH TABLES WITH READ LOCK` + 备份 binlog，不重启、不写入；",
          "2. `SHOW BINARY LOGS` + `SHOW BINLOG EVENTS` 定位误删事务起点 position；",
          "3. `mariadb-binlog --start-position=INSERT起点 --stop-position=DELETE起点` 导出闪回 SQL（只含误删前写入）；",
          "4. `--rewrite-db` 重放到恢复库验证，确认后切回生产。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-30，Ubuntu WSL + MariaDB 11.8.6）：开启 binlog（ROW）→ 建表插 5 行 → `DELETE FROM orders` 全删 → `SHOW BINLOG EVENTS` 定位（INSERT 事务 869~1335，DELETE 事务 1335 起）→ `--start-position=869 --stop-position=1335` 闪回导出（grep DELETE = 0）→ `--rewrite-db` 重放到恢复库 → 5 行 100% 恢复，内容顺序一致。",
          "- 实测复现日志（2026-08-30）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/mysql-binlog-flashback",
        "agent": "https://trove.xin/agent/problems/mysql-binlog-flashback"
      }
    },
    {
      "id": "netty-direct-memory-oom",
      "title": "Netty 直接内存 OOM：OutOfDirectMemoryError / Direct buffer memory（ByteBuf 泄漏 + MaxDirectMemorySize 未设）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "jdk"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/zhangs1986/p/22555832",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Spring Cloud Gateway（reactor-netty）类网关突然偶发 500，日志出现：",
          "",
          "```text",
          "io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 16777216 byte(s) of direct memory (used: 1006632967, max: 1013645312)",
          "```",
          "",
          "频率逐渐升高，很快服务不可用、健康检查异常。堆内存（`-Xmx`）看起来没问题，GC 日志也正常——但直接内存已经见底（used 960MB / max 967MB）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Netty 的 ByteBuf 默认走 `PooledByteBufAllocator`，即**堆外直接内存**。直接内存不受 GC 管理，分配/释放要自己负责，泄漏的 ByteBuf 不会被回收，只会一路涨到上限。三个叠加因素：",
          "",
          "| 因素 | 机制 |",
          "|---|---|",
          "| `-XX:MaxDirectMemorySize` 未显式设置 | 默认有效上限 = `-Xmx`；K8s 容器内存限制只留了 1500Mi，堆吃 1000MB 后直接内存空间紧张 |",
          "| Filter 缓存请求体无大小上限 | 50MB 上传照读进直接内存；`retain` 出的新 buffer 释放完全依赖下游消费，异常/熔断超时 → 泄漏 |",
          "| 响应式链路无兜底 | `DataBufferUtils.join` 中途断连或下游报错，中间态 buffer 变孤儿；AccessLogFilter 把响应体全量聚合进内存 |",
          "",
          "低流量时被掩盖，并发上来或几个大请求同时进来就爆。",
          "",
          "Netty 的 ByteBuf 默认走 `PooledByteBufAllocator`：内存从 JVM 堆外（direct memory）分配，**不受 GC 管理**——分配/释放全靠引用计数（refCnt），`retain` 增、`release` 减，漏一次 release 就永久泄漏，直到撞上 `MaxDirectMemorySize`。",
          "",
          "三个叠加因素各有机制：上限未显式设置时默认 = `-Xmx`（K8s 容器只给 1500Mi 时，堆 1000MB 后 direct 空间所剩无几）；Filter 缓存请求体无大小上限（50MB 照读 + 异常路径不 release → 泄漏）；响应式链路 `join` 中途断连/报错 → 中间态 buffer 成孤儿。低流量时池化缓冲复用掩盖了泄漏，并发一上来就爆。",
          "",
          "确定性推论：direct OOM 是「泄漏 + 上限」双变量——先修泄漏（`retain`/`release` 配对、异常路径 finally release），再调上限（`-XX:MaxDirectMemorySize` 与容器内存匹配）；`jcmd <pid> VM.native_memory` 看 direct 分配趋势；",
          "",
          "判断路径：堆没满却 OOM + 栈里有 Netty = direct 问题，不是 -Xmx 问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 止血：显式隔离堆与直接内存，容器限制给足",
          "java -XX:MaxDirectMemorySize=1000m -XX:+UseG1GC -Xms1000m -Xmx1000m -jar gateway.jar",
          "# K8s Deployment 内存限制 1500Mi -> 2500Mi",
          "",
          "# 根治：请求体缓存加大小门槛（超过直接跳过缓存，记 warn），响应式链路补释放兜底",
          "# - Filter 里 MAX_CACHE_BODY_SIZE = 1MB，超限跳过 body 缓存",
          "# - readBody 的 flux 链补 onErrorResume / doFinally 释放",
          "# - AccessLogFilter 同样限制聚合大小",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 看报错签名确认是直接内存 — **排查**",
          "",
          "`OutOfDirectMemoryError`（Netty 包装）或 `OutOfMemoryError: Direct buffer memory`（JDK 原生），报错里带 `used`/`max` 两个数字——先对一下 max 是不是约等于 -Xmx（没设参数时的默认）。",
          "",
          "### 2. 检查 JVM 参数与容器限制 — **排查**",
          "",
          "```bash",
          "# 确认 MaxDirectMemorySize 是否显式设置（0 = 默认取 -Xmx）",
          "java -XX:+PrintFlagsFinal -version 2>&1 | grep MaxDirectMemorySize",
          "# 对照容器 limit：堆 + 直接内存 + 元空间 + 线程栈都要装得下",
          "kubectl get deploy <name> -o yaml | grep -A2 resources",
          "```",
          "",
          "### 3. 找谁吃了直接内存 — **必做**",
          "",
          "- 网关：Netty 收发本身就用直接内存（正常）；重点查自定义 Filter：缓存请求体/响应体的 `byte[]`、`DataBufferUtils.join`、`retain` 后是否保证释放；",
          "- 开启泄漏检测（低流量窗口）：`-Dio.netty.leakDetectionLevel=paranoid`，日志会输出泄漏点；",
          "NaN",
          "",
          "### 4. 修 — **必做**",
          "",
          "- 请求体缓存加大小门槛（如 1MB），超限跳过缓存只记 warn；",
          "- 响应式链路补 `doFinally`/`onErrorResume` 保证释放；",
          "- 显式 `-XX:MaxDirectMemorySize`，按网关流量评估（堆与直接内存分离，谁也不挤谁）；",
          "- K8s 容器内存限制重新核算（堆 + 直接内存 + 元空间 + 线程栈 + 系统开销）。",
          "",
          "### 5. 验证 — **必做**",
          "",
          "压测原触发路径（大请求 + 并发），直接内存水位平稳不回涨；leak detection 无新泄漏点。"
        ],
        "怎么确认修好了": [
          "",
          "同一触发路径下不再出现 OutOfDirectMemoryError；`jcmd <pid> VM.native_memory` 或监控里 direct buffer 水位稳定；健康检查不再异常。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 网关类（Spring Cloud Gateway / reactor-netty） | 天生重度使用直接内存，必须显式 MaxDirectMemorySize + 管好 ByteBuf 释放 |",
          "| 普通服务 | 默认 = -Xmx 通常够用，但大文件/大响应体聚合进内存一样会爆 |",
          "| 未实测 | Spring Cloud Gateway + 自定义 Filter 的完整复现未在本机跑（无网关环境），核心机制（直接内存上限默认 = -Xmx、堆空闲时直接内存独立耗尽）已实测 |",
          ""
        ],
        "症状": [
          "",
          "网关偶发 500 → 频繁：日志 `io.netty.util.internal.OutOfDirectMemoryError: failed to allocate N byte(s) of direct memory (used: X, max: Y)`，服务不可用、健康检查异常；堆内存与 GC 日志正常。",
          ""
        ],
        "原因": [
          "",
          "直接内存（堆外）不受 GC 管理：`-XX:MaxDirectMemorySize` 未设置时默认 = `-Xmx`；自定义 Filter 无大小上限地缓存请求/响应体且释放依赖下游正常消费，异常/熔断时 ByteBuf 泄漏，最终顶满直接内存上限。",
          ""
        ],
        "解法": [
          "",
          "1. 显式 `-XX:MaxDirectMemorySize`（堆与直接内存隔离），容器内存限制同步给足；",
          "2. 请求体/响应体缓存加大小门槛，超限跳过；",
          "3. 响应式链路补 `doFinally`/`onErrorResume` 兜底释放；",
          "4. 开 `-Dio.netty.leakDetectionLevel=paranoid` 定位泄漏点；",
          "5. 压测验证直接内存水位。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制实测（2026-08-30，WSL Ubuntu，OpenJDK 17.0.18）：① 未设 MaxDirectMemorySize、`-Xmx64m` → 分配 64MB 直接内存后报 `OutOfMemoryError: Cannot reserve ... (allocated: 67108864, limit: 67108864)`，行为证明默认上限 = -Xmx；② `-XX:MaxDirectMemorySize=64m -Xmx256m` → 堆很空闲但直接内存照样 64MB OOM（直接内存独立于堆、不受 GC 管控）；③ 显式 256m → 分配 200MB 成功。证据：实测复现日志（2026-08-30）。",
          "- 未实测：网关 Filter/ByteBuf 泄漏完整复现（无 Spring Cloud Gateway 环境）；Netty 的 `OutOfDirectMemoryError` 与 JDK `Direct buffer memory` 同机制，Netty 只是自定义了异常包装。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/netty-direct-memory-oom",
        "agent": "https://trove.xin/agent/problems/netty-direct-memory-oom"
      }
    },
    {
      "id": "node-heap-oom",
      "title": "Node 构建/打包报 FATAL ERROR ... JavaScript heap out of memory（内存溢出）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "node",
        "npm"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/c2g5201314/p/19070905",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "前端项目打包/构建（webpack、vite、vue-tsc、jest 等）中途崩溃，控制台最后几行是 `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory`。",
          "",
          "项目越大越容易触发（依赖多/单文件大/tsc 全量类型检查）；重试可能偶发通过；发生在 `npm run build` / `vue-tsc` / CI 打包阶段。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Node 的 V8 引擎给 JS 堆设了默认上限，超过后 GC 也回收不动就放弃治疗直接崩。**默认上限不是固定的 2G**：现代 Node（64 位）默认约 4GB（本机 Node 22 实测 heap_size_limit = 4144MB），老版本或 32 位下更小（1.5~2G）——网上大量教程说的「默认 2G」是旧版本事实。构建工具要把整个依赖图 + 编译产物放内存，项目一大就顶到上限。",
          "",
          "V8 给 JS 堆设「默认上限」的动机是：堆太大时 GC 停顿（mark-sweep）会让主线程长时间卡死，宁可先 OOM 也不无限增长。上限由架构与版本决定，不是固定 2G：64 位现代 Node 默认约 4GB（Node 22 实测 heap_size_limit=4144MB），老版本/32 位更小——「默认 2G」是旧版本的事实。构建工具把整个依赖图+编译产物放内存，项目一大就顶到上限。",
          "",
          "确定性推论：OOM 先看 `heap_size_limit` 实际值（`node -e \"console.log(require('v8').getHeapStatistics.heap_size_limit)\"`），别信网上旧数字；",
          "",
          "两条正路——`--max-old-space-size` 调大上限（构建脚本 `NODE_OPTIONS=--max-old-space-size=8192`，但要匹配机器内存）或减少峰值占用（分批处理、流式）；",
          "",
          "CI 里 OOM 常见原因是容器内存限制比默认上限还小——先看容器限额；判断路径：报错 `heap out of memory` + 构建类任务 = 上限问题，`FATAL ERROR` 段会打印当时限制。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：临时提上限（值按机器内存定，如 4096/8192）",
          "NODE_OPTIONS=--max-old-space-size=8192 npm run build",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 临时提上限（最快） — **必做**",
          "",
          "三种写法等效：",
          "",
          "```bash",
          "node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build",
          "```",
          "",
          "或环境变量（CI/Jenkins/Windows 通用）：`NODE_OPTIONS=--max-old-space-size=8192`；或写进 package.json：",
          "",
          "```json",
          "\"build\": \"cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build\"",
          "```",
          "",
          "Windows 下注意引号问题可用 `cross-env`。值别超过物理内存太多，否则变 swap 卡死。",
          "",
          "### 2. 长期优化（治本） — **必做**",
          "",
          "构建缓存（webpack `cache` / vite 缓存）、代码分割/按需加载、升级构建工具（webpack→vite）、`vue-tsc --noEmit` 拆到单独阶段、依赖精简。内存压力来自\"同时放内存的东西太多\"，提上限只是把线抬高。",
          "",
          "### 3. 区分系统 OOM — **必做**",
          "",
          "若错误是 `Killed` 或 `Exit status 137`（容器/CI 里），那是操作系统杀的（cgroup/容器内存上限），调 `--max-old-space-size` 没用，要加容器内存或减并发。"
        ],
        "怎么确认修好了": [
          "",
          "提上限后构建通过（实测输出形态）：",
          "",
          "```text",
          "node --max-old-space-size=8192 <脚本> → 分配至 6000MB 上限成功退出（exit 0）",
          "NODE_OPTIONS=--max-old-space-size=8192 node <脚本> → 同样成功（exit 0，环境变量与 CLI 等效）",
          "```",
          "",
          "默认上限运行 → 复现 FATAL ERROR + SIGABRT 退出码 134（实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 默认上限 | 现代 Node 约 4GB（实测 4144MB），旧版本/32 位更小 |",
          "| 命令行 | `node --max-old-space-size=8192 ...` |",
          "| 环境变量 | `NODE_OPTIONS=--max-old-space-size=8192`（CI/Windows 通用） |",
          "| package.json | cross-env 写法避免引号问题 |",
          "| 系统 OOM | Killed/137 = 容器内存上限，不是 V8 堆 |",
          ""
        ],
        "症状": [
          "",
          "前端项目打包/构建（webpack、vite、vue-tsc、jest 等）中途崩溃，控制台最后几行是：",
          "",
          "```",
          "<--- Last few GCs --->",
          ".... MB: Mark-Compact ... allocation failure; scavenge might not succeed",
          "",
          "<--- JS stacktrace --->",
          "FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory",
          "----- Native stack trace -----",
          "```",
          "",
          "旧版 Node 上同一错误常写作 `FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`。特征：项目越大越容易触发（依赖多/单文件大/tsc 全量类型检查）；重试可能偶发通过；发生在 `npm run build` / `vue-tsc` / CI 打包阶段。",
          ""
        ],
        "原因": [
          "",
          "Node 的 V8 引擎给 JS 堆设了默认上限，超过后 GC 也回收不动就放弃治疗直接崩。**默认上限不是固定的 2G**：现代 Node（64 位）默认约 4GB（本机 Node 22 实测 heap_size_limit = 4144MB），老版本或 32 位下更小（1.5~2G）——网上大量教程说的「默认 2G」是旧版本事实。构建工具要把整个依赖图 + 编译产物放内存，项目一大就顶到上限。",
          ""
        ],
        "解法": [
          "",
          "按推荐顺序选：",
          "",
          "1. **临时提上限（最快）**，三种写法等效：",
          "   - 命令行：`node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build`（或 `webpack`/`vue-tsc` 同理）；",
          "   - 环境变量（CI/Jenkins/Windows 通用）：`NODE_OPTIONS=--max-old-space-size=8192`，Windows 永久设置用 `setx NODE_OPTIONS=--max-old-space-size=8192`（注意 setx 永久、新开窗口生效）；",
          "   - 写进 package.json：`\"build\": \"node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build\"`；Windows 下注意引号问题可用 `cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build`。",
          "   值按机器内存定（如 4096/8192），别超过物理内存太多，否则变 swap 卡死。",
          "2. **长期优化（治本）**：构建缓存（webpack `cache` / vite 缓存）、代码分割/按需加载、升级构建工具（webpack→vite）、`vue-tsc --noEmit` 拆到单独阶段、依赖精简。内存压力来自「同时放内存的东西太多」，提上限只是把线抬高。",
          "3. **区分系统 OOM**：若错误是 `Killed` 或 `Exit status 137`（容器/CI 里），那是操作系统杀的（cgroup/容器内存上限），调 `--max-old-space-size` 没用，要加容器内存或减并发。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，Node v22.22.1）：",
          "  - 默认 heap_size_limit 实测 4144MB（`v8.getHeapStatistics`），证明「默认 2G」说法对现代 Node 已过时；`--max-old-space-size=8192` 后为 8240MB；",
          "  - 构造逐步分配大数组的脚本，默认上限运行 → 完整复现 `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory`（含 Last few GCs / JS stacktrace / Native stack trace），进程 SIGABRT 退出码 134；",
          "  - `node --max-old-space-size=8192 <脚本>` → 分配至 6000MB 上限成功退出（退出码 0）；",
          "  - `NODE_OPTIONS=--max-old-space-size=8192 node <脚本>` → 同样成功（退出码 0），证明环境变量写法与 CLI 等效；",
          "  - 文章里的 package.json 改法（node 直调 vite.js）属于 CLI 写法变体，同源已验证。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/node-heap-oom",
        "agent": "https://trove.xin/agent/problems/node-heap-oom"
      }
    },
    {
      "id": "npm-digital-envelope-unsupported",
      "title": "npm run 报 Error: error:0308010C:digital envelope routines::unsupported（Node 新版本 + 老 webpack）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "node",
        "npm"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/qianlingo/p/19648349",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "用新版本 Node 跑旧项目（webpack 4 系，如老 create-react-app / vue-cli 项目）时，构建/启动即报 `Error: error:0308010C:digital envelope routines::unsupported`。",
          "",
          "报错前 0 秒、无任何业务代码改动；切到项目要求的低版本 Node（如 16.15.0）就正常。**不是项目代码的问题，是「老构建工具 + 新运行时」的兼容断层。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Node ≥17 链接的是 **OpenSSL 3**，而 OpenSSL 3 把 **MD4** 移到了 legacy provider（默认不加载）；webpack <5 默认用 `crypto.createHash('md4')` 做模块/chunk 哈希，调用时直接抛 `digital envelope routines::unsupported`。升级 Node 大版本后最容易撞上。",
          "",
          "`digital envelope routines::unsupported` 是 OpenSSL 3 的 **provider 机制**：OpenSSL 3 把 MD4/MD5 等弱算法移进 legacy provider，默认不加载；",
          "",
          "Node ≥17 链接 OpenSSL 3，而 webpack <5 用 `crypto.createHash('md4')` 做模块/chunk 哈希——算法在默认 provider 里不存在，调用即抛错。这不是业务代码的 bug，是「工具链版本组合」问题：Node 大版本升级后，底层 OpenSSL 变了，旧构建工具的行为就变了。",
          "",
          "确定性推论：升级 Node 后老项目构建报此错 = 先查构建器是否用了 md4（webpack 配置 `output.hashFunction`）；两条正路——升级 webpack（≥5 默认用更安全的算法）或显式指定 `output.hashFunction: 'sha256'`（webpack <5 可用）；",
          "",
          "`NODE_OPTIONS=--openssl-legacy-provider` 是「重新加载 legacy provider」的临时逃生门，能过但弱算法回归，只用于没法升级的存量项目；判断路径：报错栈里出现 createHash/md4 即坐实。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：临时兼容——启用 legacy provider 让 OpenSSL 重新提供 MD4",
          "NODE_OPTIONS=--openssl-legacy-provider npm run build",
          "```",
          "",
          "只作过渡；长期请升级构建工具。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 长期正解：升级构建工具 — **必做**",
          "",
          "webpack 升级到 ≥5（5.61+ 自带 wasm 哈希回退，不再依赖 OpenSSL MD4）；create-react-app/vue-cli 项目跟随官方升级脚本（`npm i -D webpack@5` / `npx create-react-app@latest` 等）。",
          "",
          "### 2. 临时兼容（最快见效） — **必做**",
          "",
          "```bash",
          "NODE_OPTIONS=--openssl-legacy-provider npm run build",
          "```",
          "",
          "或写入 `.env`（老 CRA 项目会读取）：`echo NODE_OPTIONS=--openssl-legacy-provider >> .env`。注意：Node 17.0–17.2 的 NODE_OPTIONS 不允许该开关，用命令行参数 `node --openssl-legacy-provider` 或换 Node 版本。legacy provider 安全性弱于默认，只作过渡。",
          "",
          "### 3. 换 Node 版本（文章方案） — **必做**",
          "",
          "```bash",
          "nvm use 16.15.0",
          "```",
          "",
          "切到项目锁定的旧版本后正常——治标，掩盖了\"工具链过旧\"的根因，长期仍建议升级。"
        ],
        "怎么确认修好了": [
          "",
          "md4 可用（实测输出形态）：",
          "",
          "```text",
          "NODE_OPTIONS=--openssl-legacy-provider node -e \"crypto.createHash('md4').update('x').digest('hex')\" → 输出摘要 866437cb...（实测）",
          "```",
          "",
          "对照组：默认配置 `createHash('md4')` → `error:0308010C`（实测复现，堆栈与文章逐字一致）；`createHash('sha256')` 正常（md4 专属问题）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Node ≥17 + OpenSSL 3 | md4 在 legacy provider，默认不可用 |",
          "| webpack <5 | 用 md4 做哈希，必撞 |",
          "| webpack ≥5.61 | wasm 哈希回退，无需处理 |",
          "| Node 17.0–17.2 | NODE_OPTIONS 不允许该开关，用命令行参数 |",
          "| 换 Node 版本 | nvm use 旧版本，治标 |",
          ""
        ],
        "症状": [
          "",
          "用新版本 Node 跑旧项目（webpack 4 系，如老 create-react-app / vue-cli 项目）时，构建/启动即报：",
          "",
          "```",
          "ERROR  Error: error:0308010C:digital envelope routines::unsupported",
          "Error: error:0308010C:digital envelope routines::unsupported",
          "       at new Hash (node:internal/crypto/hash:101:19)",
          "       at Object.createHash (node:crypto:139:10)",
          "```",
          "",
          "典型场景：本机 Node 22.x 跑项目，报错前 0 秒、无任何业务代码改动；切到项目要求的低版本 Node（如 16.15.0）就正常。",
          ""
        ],
        "原因": [
          "",
          "Node ≥17 链接的是 **OpenSSL 3**，而 OpenSSL 3 把 **MD4** 移到了 legacy provider（默认不加载）；webpack <5 默认用 `crypto.createHash('md4')` 做模块/chunk 哈希，调用时直接抛 `digital envelope routines::unsupported`。这不是项目代码的问题，是「老构建工具 + 新运行时」的兼容断层（升级 Node 大版本后最容易撞上）。",
          ""
        ],
        "解法": [
          "",
          "按推荐顺序选：",
          "",
          "1. **长期正解：升级构建工具** —— webpack 升级到 ≥5（5.61+ 自带 wasm 哈希回退，不再依赖 OpenSSL MD4）；create-react-app/vue-cli 项目跟随官方升级脚本（`npm i -D webpack@5` / `npx create-react-app@latest` 等）。",
          "2. **临时兼容（最快见效）：启用 legacy provider**，让 OpenSSL 重新提供 MD4：",
          "   ```bash",
          "   # 单次命令",
          "   NODE_OPTIONS=--openssl-legacy-provider npm run build",
          "   # 或写入 .env（老 CRA 项目会读取）",
          "   echo NODE_OPTIONS=--openssl-legacy-provider >> .env",
          "   ```",
          "   注意：Node 17.0–17.2 的 NODE_OPTIONS 不允许该开关，用命令行参数 `node --openssl-legacy-provider` 或换 Node 版本。legacy provider 安全性弱于默认，只作过渡。",
          "3. **换 Node 版本（文章方案）**：nvm 切到项目锁定的旧版本（如 `nvm use 16.15.0`）后正常——治标，掩盖了「工具链过旧」的根因，长期仍建议升级。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，Node v22.22.1 / npm 9.2.0 / OpenSSL 3.5.5，与文章报错环境同代）：",
          "  - `node -e \"crypto.createHash('md4')...\"` → 完整复现 `error:0308010C:digital envelope routines::unsupported`，堆栈与文章逐字一致（`new Hash (node:internal/crypto/hash:101:19)`）；",
          "  - 对照组：同一进程 `createHash('sha256')` 正常输出摘要 —— 证明是 md4 专属问题，不是 crypto 全挂；",
          "  - `NODE_OPTIONS=--openssl-legacy-provider node ...` 与 `node --openssl-legacy-provider ...` → md4 均成功输出摘要 `866437cb...`（两种传法都验证）；",
          "  - `openssl version` = 3.5.5，`openssl list -digest-algorithms` 中 MD4 仅在 legacy 条目（RSA-MD4 => MD4）——佐证「默认 provider 不含 md4」。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/npm-digital-envelope-unsupported",
        "agent": "https://trove.xin/agent/problems/npm-digital-envelope-unsupported"
      }
    },
    {
      "id": "npm-eresolve-peer-conflict",
      "title": "npm install 报 npm ERR! code ERESOLVE / ERESOLVE unable to resolve dependency tree（peer 依赖冲突）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "node",
        "npm"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/yu-ch/p/15942844.html",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "老前端项目（Vue2/webpack 4 时代）换电脑或换 Node 大版本后 `npm install` 报 `ERESOLVE unable to resolve dependency tree`，代码一行没动，依赖就是装不上。错误信息里其实写清楚了冲突双方（`Found: ...` 和 `peer ... from ...`）——先读报错，别急着删 node_modules。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "npm 7+ 默认**严格解析 peerDependencies**：自动安装 peer 依赖，版本不满足直接拒绝整棵依赖树。而 npm 6 时代 peer 不匹配只是静默跳过——所以同一份 package.json 在旧 npm 上\"正常\"。常见触发：",
          "",
          "- **插件版本过旧**：插件声明的 peer 范围不含新版主依赖（如 eslint-plugin-import@2.22.1 的 peer 是 eslint `^2~^7.2.0`，装 eslint 8 即冲突）；",
          "- 两个依赖对同一 peer 要求互相矛盾，npm 无法同时满足；",
          "- 换机器后 node/npm 版本变新，触发同样的解析逻辑差异。",
          "",
          "> 实测环境：Node v22.22.1 / npm 9.2.0，2026-08-29。",
          "",
          "npm 7+ 的解析器把 peerDependencies 当**硬约束**：安装时自动解析并安装 peer 依赖，版本不满足就拒绝整棵依赖树（ERESOLVE）；npm 6 及以前 peer 不匹配只是静默跳过（甚至不装）——同一份 package.json 在不同 npm 版本上行为不同，这是「换机器/升级后突然报错」的根因。",
          "",
          "ERESOLVE 的常见形态：插件声明的 peer 范围太旧（不含新版主依赖）、两个依赖对同一 peer 要求互相矛盾（npm 无法同时满足）。",
          "",
          "确定性推论：ERESOLVE 是「解析器说这套组合无解」，不是「某个包坏了」；处理优先级——升级冲突方（插件或主依赖到互相兼容的版本）> 用 `overrides` 显式钉住 peer 版本 > `--legacy-peer-deps`（退回 npm6 静默行为，最后手段）；修完 `npm ls` 看依赖树是否一致；",
          "",
          "判断路径：报错信息里的 `While resolving` 链就是冲突双方，顺着升级。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：跳过 peer 严格检查（旧项目过渡首选）",
          "npm install --legacy-peer-deps",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 读报错定位冲突（正解） — **排查**",
          "",
          "```bash",
          "npm view <插件> peerDependencies",
          "```",
          "",
          "`Found: ...` 和 `peer ... from ...` 就是冲突双方。优先把插件升级到支持新版主依赖的版本，或把主依赖降回 peer 范围内。",
          "",
          "### 2. 快速绕过 — **必做**",
          "",
          "```bash",
          "npm install --legacy-peer-deps",
          "```",
          "",
          "接受旧 npm 的语义（`--force` 同理但更粗暴）。注意这等于接受\"不正确且可能损坏\"的依赖解析，装完运行时可能仍有兼容问题，只作过渡。",
          "",
          "### 3. 旧项目整体重装 — **必做**",
          "",
          "删 `node_modules` + `package-lock.json` 后重装；或临时用 npm 6 语义：",
          "",
          "```bash",
          "npx -p npm@6 npm install --legacy-peer-deps",
          "```",
          "",
          "无需卸载 npm 7+，npx 指定版本即可（实测：本机 npm 9.2.0 vs npx npm@6 = 6.14.18）。",
          "",
          "### 4. 团队默认跳过检查（治标） — **排查**",
          "",
          "项目级 `.npmrc` 写 `legacy-peer-deps=true`；长期仍建议升依赖。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "npm install --legacy-peer-deps && npm ls --depth=0",
          "```",
          "",
          "依赖树能完整装上即成功。实测：`eslint@8.0.0` + `eslint-plugin-import@2.22.1` → 完整复现 ERESOLVE 报错链（exit 1）；`--legacy-peer-deps` → added 232 packages（exit 0）；`--force` → 同样成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Vue2/webpack 4 老项目 | 插件 peer 范围旧，`--legacy-peer-deps` 过渡 |",
          "| 新机器（Node 大版本升级） | npm 7+ 严格解析触发，同上 |",
          "| CI | 项目级 `.npmrc` 写 `legacy-peer-deps=true` |",
          "| 长期 | 升级插件/主依赖到相互兼容的版本 |",
          ""
        ],
        "症状": [
          "",
          "老前端项目（Vue2/webpack 4 时代）`npm install` 报：",
          "",
          "```",
          "npm ERR! code ERESOLVE",
          "npm ERR! ERESOLVE unable to resolve dependency tree",
          "npm ERR! While resolving: <你的项目>@1.0.0",
          "npm ERR! Found: <已装的主依赖>@<版本>",
          "npm ERR! Could not resolve dependency:",
          "npm ERR! peer <peer 名>@\"<peer 版本范围>\" from <插件>@<版本>",
          "```",
          "",
          "特征：换电脑/换 Node 大版本后最容易出现；项目代码没动过，但依赖就是装不上；错误信息里明确写了冲突双方（Found: ... / peer ... from ...）。",
          ""
        ],
        "原因": [
          "",
          "npm 7+ 默认**严格解析 peerDependencies**：会自动安装 peer 依赖，并在版本不满足时直接拒绝整棵依赖树。而 npm 6 时代 peer 不匹配只是静默跳过不检查，所以同一份 package.json 在旧 npm 上\"正常\"。常见触发：",
          "",
          "1. **插件版本过旧**：插件声明的 peer 范围不含新版主依赖（如 eslint-plugin-import@2.22.1 的 peer 是 eslint ^2~^7.2.0，装 eslint 8 即冲突）；",
          "2. **两个依赖对同一 peer 要求互相矛盾**，npm 无法同时满足；",
          "3. 换机器后 node/npm 版本变新，触发同样的解析逻辑差异。",
          ""
        ],
        "解法": [
          "",
          "按推荐顺序选：",
          "",
          "1. **看错误信息定位冲突，修版本（正解）**：错误里的 `Found: ...` 和 `peer ... from ...` 就是冲突双方。优先把插件升级到支持新版主依赖的版本，或把主依赖降回 peer 范围内（`npm view <包> peerDependencies` 查范围）。",
          "2. **快速绕过（最常用）**：`npm install --legacy-peer-deps` —— 跳过 peer 严格检查，接受旧 npm 的语义；`--force` 同理但更粗暴。注意这等于接受\"不正确且可能损坏\"的依赖解析，装完运行时可能仍有兼容问题，只作过渡。",
          "3. **旧项目整体重装**：删 `node_modules` + `package-lock.json` 后重装；或临时用 npm 6 语义：`npx -p npm@6 npm install --legacy-peer-deps`（无需卸载 npm 7+，npx 指定版本即可）。",
          "4. 若想彻底规避：项目级 `.npmrc` 写 `legacy-peer-deps=true`，让团队默认跳过检查（治标，长期仍建议升依赖）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，Node v22.22.1 / npm 9.2.0）：",
          "  - 构造真实冲突：`eslint@8.0.0` + `eslint-plugin-import@2.22.1`（`npm view` 确认 peer 为 `^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0`，不含 8）→ `npm install --dry-run` 完整复现 ERESOLVE 报错链（Found: eslint@8.0.0 / Could not resolve dependency: peer eslint@... from eslint-plugin-import@2.22.1），退出码 1；",
          "  - `--legacy-peer-deps` → 解析成功（added 232 packages），退出码 0；",
          "  - 清掉 node_modules/lock 后 `--force` → 解析成功（added 232 packages），退出码 0；",
          "  - 版本语义对照：本机 npm 9.2.0 vs `npx -p npm@6 npm --version` = 6.14.18（npm 6 不检查 peer，故旧项目当年\"正常\"）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/npm-eresolve-peer-conflict",
        "agent": "https://trove.xin/agent/problems/npm-eresolve-peer-conflict"
      }
    },
    {
      "id": "npm-global-eacces",
      "title": "npm install -g 报 npm ERR! code EACCES permission denied（全局安装权限）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "npm"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/vuejs/vue-cli/issues/1809",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`npm install -g <包>` 报 `npm ERR! code EACCES`，你以为权限不够加个 `sudo` 就完事——结果下次连缓存都写不进（`Your cache folder contains root-owned files`），越修越糟。其实报错有**两种形态**，路径不同、修法完全不同，先看 `path` 指向哪再动手。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "npm 全局安装要写两个位置：全局包目录（`npm prefix -g`，如 `/usr/local/lib/node_modules`）和缓存（`~/.npm`）：",
          "",
          "| 形态 | path 指向 | 根因 |",
          "|---|---|---|",
          "| 形态 1 | `/usr/local/lib/node_modules/...` | 全局目录属 root，当前用户非 root |",
          "| 形态 2 | `/home/<user>/.npm/_cacache` | 曾用 `sudo npm install`，`~/.npm` 混入 root 文件 |",
          "",
          "形态 1 是**非 root 用户装全局包**的常态（系统 node 装在 `/usr/local` 或 `/usr`）；形态 2 是**曾经 sudo 装过的后遗症**。报错都带 `EACCES` + `errno -13`，但解法不同。",
          "",
          "> 实测环境：WSL Ubuntu，node v22.22.1 / npm 9.2.0，2026-08-29。",
          "",
          "npm 全局安装写两个位置：全局包目录（`npm prefix -g` 指向的 node_modules）和缓存目录（`~/.npm`）。",
          "",
          "EACCES 是「当前用户对目标目录无写权限」：形态 1 的全局目录属 root（系统 node 装进 /usr/local 或 /usr），非 root 用户写入被拒；形态 2 是历史 `sudo npm install` 把 `~/.npm` 缓存目录的属主变成了 root，之后普通用户写缓存被拒。",
          "",
          "两种形态都是 EACCES/errno -13，但根因和解法完全不同——形态 1 是「目录属主」问题，形态 2 是「缓存属主被污染」。",
          "",
          "确定性推论：报错路径带 `node_modules` = 形态 1，带 `_cacache`/`.npm` = 形态 2；形态 1 的正解是 `npm config set prefix ~/.npm-global` 把全局目录挪到用户目录并加 PATH（不要 sudo 硬装），形态 2 的正解是 `sudo chown -R $(whoami) ~/.npm` 把缓存属主改回来；",
          "",
          "判断路径固定：`ls -ld` 看目标目录属主，再对照报错路径。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键（形态 1，推荐）：全局 prefix 改到用户目录，一劳永逸",
          "npm config set prefix ~/.npm-global && export PATH=\"$HOME/.npm-global/bin:$PATH\" && npm install -g <包>",
          "```",
          "",
          "形态 2 用：`sudo chown -R $(whoami) ~/.npm`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看报错的 path 判断形态 — **排查**",
          "",
          "```bash",
          "npm install -g yaml 2>&1 | grep -E \"path|EACCES\"",
          "```",
          "",
          "`path /usr/local/lib/...` → 形态 1；`path /home/<user>/.npm/_cacache` → 形态 2。",
          "",
          "### 2. 形态 1：改全局 prefix 到用户目录（推荐） — **必做**",
          "",
          "```bash",
          "npm config set prefix ~/.npm-global",
          "export PATH=\"$HOME/.npm-global/bin:$PATH\"",
          "```",
          "",
          "把 `export PATH=...` 写进 `~/.bashrc`，以后全局包都装到自己目录，不再碰系统目录。",
          "",
          "### 3. 形态 2：把 ~/.npm 所有权还给自己 — **必做**",
          "",
          "```bash",
          "sudo chown -R $(whoami) ~/.npm",
          "```",
          "",
          "npm 报错里会直接提示对应的 chown 命令。不想保留旧缓存也可以 `rm -rf ~/.npm` 让它重建。",
          "",
          "### 4. 备选方案 — **进阶**",
          "",
          "- 用 nvm 管理 node：node 装在 `~/.nvm` 下，全局包天然可写；",
          "- `sudo npm install -g` 能用但不推荐——会继续制造形态 2。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "npm install -g yaml && yaml --help",
          "```",
          "",
          "不再报 EACCES、`yaml` 命令能跑（输出 usage）即成功。实测：形态 1 修复后 `added 1 package` 成功；形态 2 `chown` 后 `changed 1 package` 成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Linux（系统 node 在 /usr/local） | 形态 1 高发，改 prefix 到 ~/.npm-global |",
          "| WSL Ubuntu | 与原生 Linux 一致；注意 sudo 残留同样制造形态 2 |",
          "| macOS（Homebrew node） | 目录属 brew 用户，同样建议 nvm 或改 prefix |",
          "| 曾经 sudo 装过包 | 必查 `~/.npm` 属主，`chown -R $(whoami) ~/.npm` |",
          ""
        ],
        "症状": [
          "",
          "`npm install -g <包>` 报权限错误，两种典型形态：",
          "",
          "```",
          "# 形态 1：全局包目录没写权限（最常见）",
          "npm ERR! code EACCES",
          "npm ERR! syscall mkdir",
          "npm ERR! path /usr/local/lib/node_modules/yaml",
          "npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/yaml'",
          "",
          "# 形态 2：缓存目录被 root 占用（用 sudo 装过之后）",
          "npm ERR! code EACCES",
          "npm ERR! syscall mkdir",
          "npm ERR! path /home/<user>/.npm/_cacache",
          "npm ERR! Your cache folder contains root-owned files, due to a bug in",
          "npm ERR! previous versions of npm which has since been addressed.",
          "```",
          "",
          "特征：错误都带 `npm ERR! code EACCES` + `errno -13`；退出码常为 243；形态 1 发生在**非 root 用户**装全局包（系统 node 装在 `/usr/local` 或 `/usr`，目录属 root）；形态 2 常见于**曾经 `sudo npm install`** 之后（`~/.npm` 里混入 root 文件，普通用户再也写不进去）。",
          ""
        ],
        "原因": [
          "",
          "npm 全局安装要写两个位置：全局包目录（`npm prefix -g`，如 `/usr/local/lib/node_modules`）和缓存（`~/.npm`）。前者属 root 而当前用户非 root → 形态 1；后者被 root 建过文件/目录（sudo 残留）→ 形态 2。报错虽都是 EACCES，但路径不同、修法不同——先看 `path` 指向哪里。",
          ""
        ],
        "解法": [
          "",
          "1. **形态 1：把全局 prefix 改到用户目录（推荐，一劳永逸）**：",
          "   ```",
          "   npm config set prefix ~/.npm-global",
          "   export PATH=\"$HOME/.npm-global/bin:$PATH\"   # 写入 ~/.bashrc",
          "   ```",
          "   之后 `npm install -g` 装到自己的目录，不再碰系统目录。",
          "2. **形态 1 备选**：用 nvm 管理 node（node 装在自己的 ~/.nvm 下，全局包天然可写）；或 `sudo npm install -g`（能用但不推荐，会继续制造形态 2）。",
          "3. **形态 2：把 ~/.npm 所有权还给自己**：",
          "   ```",
          "   sudo chown -R $(whoami) ~/.npm",
          "   ```",
          "   （npm 报错里会直接提示对应的 chown 命令）。不想保留旧缓存也可以 `rm -rf ~/.npm` 让它重建。",
          "4. **验证**：`npm install -g <包>` 成功 + 命令能跑（如 `yaml` 输出 usage）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，node v22.22.1 / npm 9.2.0，全局 prefix /usr/local；临时用户 <user>）：",
          "  - 形态 1 复现：<user> 执行 `npm install -g yaml` → `EACCES: permission denied, mkdir '/usr/local/lib/node_modules/yaml'`（exit 243）；",
          "  - 形态 1 修复：`npm config set prefix ~/.npm-global` + PATH → 同命令 `added 1 package` 成功，`yaml` 命令可执行（输出 usage）；",
          "  - 形态 2 复现：把 `/home/<user>/.npm` chown 成 root → 再装 → `EACCES ... /home/<user>/.npm/_cacache`，npm 自提示 `sudo chown -R 1002:1002 \"/home/<user>/.npm\"`（exit 243）；",
          "  - 形态 2 修复：`chown -R <user>:<user> /home/<user>/.npm` → `changed 1 package` 成功；",
          "  - 临时用户与缓存均已清理（实测环境无残留）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/npm-global-eacces",
        "agent": "https://trove.xin/agent/problems/npm-global-eacces"
      }
    },
    {
      "id": "npm-mirror-registry",
      "title": "npm install 慢/随机挂起/网络错误（ETIMEDOUT / ECONNRESET / hang forever）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "node",
        "npm"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/npm/cli/issues/4028",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`npm install` 在国内网络下表现各异：报 `ETIMEDOUT` / `ECONNRESET` / `fetch failed`；或卡在某个包不动、CTRL+C 都杀不掉；或很慢但 `curl` 官方源却正常。网上教程一律让你换淘宝镜像，但你换上可能更慢——**镜像不是永远更快，得先测再用**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "四个叠加因素：",
          "",
          "1. **官方 registry 网络波动**：国内访问 `registry.npmjs.org` 时好时坏（CDN 节点波动）；",
          "2. **安全审计拖慢**：npm 默认装完要 POST 到 `npm/v1/security/advisories/bulk` 做漏洞检查，弱网下能把安装拖到几十秒；",
          "3. **配置残留**：`~/.npmrc` 里旧 registry / 代理设置冲突；",
          "4. **挂死**：底层 fetch 在弱网 + 重试组合下进入不可中断状态（[npm/cli#4028](https://github.com/npm/cli/issues/4028)，226 comments，Windows 上尤其难杀）。",
          "",
          "> 实测环境：WSL Ubuntu，node v22.22.1 / npm 9.2.0，校园网无代理，2026-08-29。",
          "",
          "npm install 慢/卡是四个独立环节叠加：①registry 寻址（registry.npmjs.org 国内无稳定 CDN）；②安全审计请求（npm 默认装完 POST /npm/v1/security/advisories/bulk，弱网下拖几十秒）；③配置残留（~/.npmrc 的旧 registry/代理冲突，会让请求走错误路径而不是报错）；",
          "",
          "④fetch 在弱网+重试下进入不可中断状态（[npm/cli#4028](https://github.com/npm/cli/issues/4028)，Windows 上尤其难杀）。",
          "",
          "四者都表现为「慢/卡」，但修法不同。",
          "",
          "确定性推论：先分层——`npm config get registry` 看当前源、`npm ping` 测 registry 延迟；换源（npmmirror 等）解决 ①，`npm install --no-audit` 关审计解决 ②（CI 里可默认关），清理 ~/.npmrc 解决 ③，④只能升级 npm 或换终端杀进程；改完 `npm config list` 确认无残留覆盖；",
          "",
          "判断路径：卡在 security advisories = ②，卡在 fetch = ①④。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先关审计再装（低成本高收益），并设置更长超时",
          "npm install --no-audit --no-fund --fetch-timeout 600000 <包>",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先测再换源，别盲信教程 — **必做**",
          "",
          "```bash",
          "time npm ping --registry https://registry.npmjs.org",
          "time npm ping --registry https://registry.npmmirror.com",
          "```",
          "",
          "本机 2026-08-29 实测：ping 官方 1.7s / 镜像 0.8s，但装 typescript 官方 8.6s / 镜像 13.3s——**镜像不是永远更快，当前网络谁快用谁**。",
          "",
          "### 2. 换源 — **必做**",
          "",
          "```bash",
          "npm install --registry https://registry.npmmirror.com <包>   # 一次性",
          "npm config set registry https://registry.npmmirror.com        # 持久化（写入 ~/.npmrc）",
          "npm config set registry https://registry.npmjs.org            # 还原",
          "```",
          "",
          "### 3. 慢先关 audit/fund — **必做**",
          "",
          "```bash",
          "npm install --no-audit --no-fund",
          "```",
          "",
          "或写进 `~/.npmrc`：`audit=false fund=false`。",
          "",
          "### 4. 调超时/缓存 — **必做**",
          "",
          "```bash",
          "npm config set fetch-timeout 600000",
          "npm config set fetch-retries 5",
          "npm cache verify",
          "```",
          "",
          "别上来就 `cache clean --force`（全量重下更慢）。",
          "",
          "### 5. 挂死处理 — **必做**",
          "",
          "先确认是网络（`curl -I https://registry.npmjs.org` 计时）；Windows 上 `taskkill /F /IM node.exe` 兜底；经常挂死考虑 pnpm/yarn（快速失败、缓存策略不同）。",
          "",
          "> 安全提醒：`~/.npmrc` 常含登录 `_authToken`，贴日志/提 issue 前必须脱敏——本条目实测时就踩过这个坑（已脱敏）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "time npm install typescript --no-audit --no-fund",
          "```",
          "",
          "能稳定装完、耗时可接受即成功。实测：官方源 8.6s / npmmirror 13.3s（当日镜像反而慢）；`npm config get registry` 可确认当前源。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 校园网/家宽（无代理） | 官方源时好时坏，先 ping 对比再换源 |",
          "| Windows | 挂死难杀，`taskkill /F /IM node.exe` 兜底 |",
          "| CI | 项目根放 `.npmrc` 或 `--registry` 参数最明确 |",
          "| 有稳定代理 | 走代理直连官方源即可，不必换源 |",
          ""
        ],
        "症状": [
          "",
          "`npm install` 表现各异：",
          "",
          "```",
          "npm ERR! code ETIMEDOUT / ECONNRESET / fetch failed   # 网络类报错",
          "npm install 卡在某个包不动，CTRL+C 无效            # [npm/cli#4028](https://github.com/npm/cli/issues/4028)（226 comments）",
          "npm install 很慢，但 curl 官方源却正常             # 慢在依赖解析/安全审计",
          "```",
          "",
          "特征：官方源 `registry.npmjs.org` 对国内网络时好时坏（CDN 节点波动）；",
          "挂死场景 Windows 上尤其难杀（#4028 里进程杀不掉）；慢不一定在下载，可能在 audit。",
          "",
          "同族但根因不同（别在本页死磕网络）：",
          "",
          "- `EACCES` / `EPERM`（全局安装/缓存写入权限）→ 是权限问题不是网络问题，Permission denied 家族见 [脚本/文件权限](script-permission-denied.html)；",
          "- `npm ping` 两源都正常但还是很慢 → 慢在 audit/解析环节，先关审计（`--no-audit --no-fund`）而不是换源。",
          ""
        ],
        "原因": [
          "",
          "① 官方 registry 网络波动：国内访问 registry.npmjs.org 不稳定，下载/元数据请求超时重试；",
          "② 安全审计拖慢：npm 默认装完 POST 到 `npm/v1/security/advisories/bulk` 做漏洞检查（[npm/cli#7383](https://github.com/npm/cli/issues/7383)，10 comments），",
          "   该请求在弱网下能把安装拖到几十秒；",
          "③ 配置残留：~/.npmrc 里旧 registry / 代理设置冲突；",
          "④ 挂死：底层 fetch 在弱网+重试组合下进入不可中断状态（#4028）。",
          ""
        ],
        "解法": [
          "",
          "1. **先测再换源，别盲信教程**（网络每天都在变）：",
          "   `time npm ping --registry https://registry.npmjs.org` 与 `time npm ping --registry https://registry.npmmirror.com` 对比。",
          "   本机 2026-08-29 实测：ping 官方 1.7s / 镜像 0.8s，但装 typescript 官方 8.6s / 镜像 13.3s——",
          "   镜像不是永远更快，当前网络谁快用谁。",
          "2. **换源**：一次性 `npm install --registry https://registry.npmmirror.com ...`；",
          "   持久化 `npm config set registry https://registry.npmmirror.com`（写入 ~/.npmrc），还原 `npm config set registry https://registry.npmjs.org`。",
          "3. **慢先关 audit/fund**（低成本高收益）：`npm install --no-audit --no-fund`，或写入 ~/.npmrc 的 `audit=false fund=false`。",
          "4. **调超时/重试**：`npm config set fetch-timeout 600000`、`fetch-retries 5`；",
          "   缓存问题先 `npm cache verify`，别上来就 `cache clean --force`（全量重下更慢）。",
          "5. **挂死处理**：先确认是网络（`curl -I https://registry.npmjs.org` 计时）；",
          "   Windows 上 `taskkill /F /IM node.exe` 兜底；经常挂死考虑 pnpm/yarn（快速失败、缓存策略不同）。",
          "6. **CI/项目级**：项目根放 `.npmrc` 配 registry 可随仓库走；CI 用 `--registry` 参数最明确。",
          "7. **安全提醒**：~/.npmrc 常含登录 `_authToken`，贴日志/提 issue 前必须脱敏——本条目实测时就踩了这个坑（已脱敏）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，node v22.22.1 / npm 9.2.0，校园网无代理）：",
          "  - 基线：`npm config get registry` = https://registry.npmjs.org/；",
          "  - ping 对比：官方 PONG 1483ms（总 1.7s）/ npmmirror PONG（总 0.8s）；",
          "  - 全量安装对比（隔离缓存，typescript，31MB node_modules）：官方源 8.6s / npmmirror 13.3s——当日镜像反而慢，实测否定「镜像必快」；",
          "  - 持久化：`npm config set registry https://registry.npmmirror.com` → `npm config get registry` 生效，~/.npmrc 出现 registry 行；验证后还原官方源；",
          "  - 事故记录：grep ~/.npmrc 时把 `_authToken` 带进了本地记录 → 立即脱敏为 `<REDACTED>`（教训见解法 7）。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「同根（国内换源家族）」读：",
          "",
          "- [pip 安装超时/慢](pip-mirror-source.html)（同根：换源家族，python 侧）",
          "- [Go 模块拉取慢/超时](go-proxy-cn.html)（同根：换源家族，go 侧）",
          "- [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（同根：换源家族，容器侧）",
          "- [GitHub 直连慢/失败](github-direct-connect-fail.html)（网络层同根：ECONNRESET 类）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/npm-mirror-registry",
        "agent": "https://trove.xin/agent/problems/npm-mirror-registry"
      }
    },
    {
      "id": "oh-my-claudecode-1",
      "title": "命令未找到（omc: command not found）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#1",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "装完后 `omc` 报 command not found；或 `/omc-setup` 后 CLI 仍不可用。装了却找不到命令，常见原因有三个，按下面排查。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 原因 | 说明 |",
          "|---|---|",
          "| npm 全局 bin 不在 PATH | npm prefix 自定义场景 |",
          "| 装的是插件形态 | 没跑 `/omc-setup` 生成 CLI 入口 |",
          "| 安装中途失败 | 网络/依赖问题导致半成品 |",
          "",
          "“命令找不到”的本质是**shell 解析链**问题：你输入 `omc`，shell 按 PATH 环境变量依次查找可执行文件，全路径都找不到就报 command not found。",
          "",
          "所以三个常见原因对应三个不同的断点：**装错形态**——OMC 有两种安装形态（Claude Code 插件 vs npm CLI），插件形态不会生成 `omc` 命令，只有跑 `/omc-setup` 才会生成 CLI 入口（断点在“命令根本不存在”）；",
          "",
          "**装到别处**——npm 全局 bin 目录（`$(npm prefix -g)/bin`）不在 PATH 里，命令存在但 shell 找不到（断点在“查找路径”）；",
          "",
          "**装了一半**——安装中途失败，bin 链接没生成（断点在“安装完整性”）。排查顺序固定：先 `npm ls -g` 确认包在不在（排除“没装”）→ 重跑 setup 或重装（补全缺失环节）→ 最后 `command -v omc` 查 PATH（确认查找链）。三层都过了，命令必在。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认安装 → 重跑 setup 或重装 CLI → 检查 PATH",
          "npm ls -g oh-my-claude-sisyphus && npm i -g oh-my-claude-sisyphus@5.0.0 --registry=https://registry.npmmirror.com && command -v omc",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认安装 — **必做**",
          "",
          "```bash",
          "npm ls -g oh-my-claude-sisyphus",
          "```",
          "",
          "### 2. 重跑 /omc-setup（插件形态）或重装 CLI — **必做**",
          "",
          "```bash",
          "npm i -g oh-my-claude-sisyphus@5.0.0",
          "```",
          "",
          "国内加 `--registry=https://registry.npmmirror.com`。",
          "",
          "### 3. PATH 检查 — **排查**",
          "",
          "```bash",
          "command -v omc",
          "```",
          "",
          "npm prefix 自定义时把 `$(npm prefix -g)/bin` 加入 PATH。"
        ],
        "怎么确认修好了": [
          "",
          "命令面就位（实测输出形态）：",
          "",
          "```text",
          "command -v omc",
          "→ /usr/local/bin/omc（隔离 HOME 下实测）",
          "```",
          "",
          "`omc --version` 正常输出版本。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| npm prefix 自定义 | 把 `$(npm prefix -g)/bin` 加入 PATH |",
          "| 插件形态 | `/omc-setup` 生成 CLI 入口（CSDN 2026-03 案例） |",
          "| 安装半途失败 | 重装 CLI（国内加镜像） |",
          "| Windows / macOS | 路径对应各平台 npm 全局目录 |",
          ""
        ],
        "症状": [
          "",
          "",
          "装完后 `omc` 报 command not found；或 `/omc-setup` 后 CLI 仍不可用。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "1. npm 全局 bin 目录不在 PATH（npm prefix 自定义场景）；",
          "2. 装的是 Claude Code 插件形态，没跑 `/omc-setup` 生成 CLI 入口；",
          "3. 安装中途失败（网络/依赖）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 确认安装：`npm ls -g oh-my-claude-sisyphus`。",
          "2. 重跑 `/omc-setup`（Claude Code 插件形态）；或重装 CLI：`npm i -g oh-my-claude-sisyphus@5.0.0`（国内加 `--registry=https://registry.npmmirror.com`）。",
          "3. PATH 检查：`command -v omc`；npm prefix 自定义时把 `$(npm prefix -g)/bin` 加入 PATH。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN OMC 教程常见问题（命令未找到 → omc-setup，2026-03）。",
          "本站实测记录"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-1",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-1"
      }
    },
    {
      "id": "oh-my-claudecode-2",
      "title": "钩子未执行（hook 不触发 / Windows hook 报错）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Agent 会话里 OMC 的 hook 不触发；Windows 上安装后报 `UserPromptSubmit/Stop hook error`（Issue #30 场景）。配置了却没生效。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 原因 | 说明 |",
          "|---|---|",
          "| hook 脚本无执行权限 | `~/.claude/hooks/**/*.sh` 缺 +x |",
          "| Windows shell 差异 | 路径/转义差异导致 hook 调用失败 |",
          "| 插件重装残留 | 旧 hook 残留与新安装冲突 |",
          "",
          "hook 的触发是一条**前置链**，三个条件缺一不可：Claude Code 在事件发生时（提交提示词/会话停止）按配置找到 hook 脚本 → 脚本必须有执行权限（+x，否则 exec 直接拒绝）→ 脚本的 shebang 解释器要能跑起来（Windows 上 shebang 通常是 `#!/bin/bash`，原生 cmd 环境没有 bash，必须 Git Bash/WSL；",
          "",
          "CRLF 行尾还会让 shebang 解析失败）。",
          "",
          "任何一个前置不满足，表现都是\"配置了却没触发\"或\"hook error\"。所以排查顺序固定：先权限、再解释器环境、最后才怀疑残留——多数情况第一步就解决了。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：补执行权限（最常见修复）",
          "find ~/.claude/hooks -name '*.sh' -exec chmod +x {} +",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 补执行权限 — **必做**",
          "",
          "```bash",
          "find ~/.claude/hooks -name '*.sh' -exec chmod +x {} +",
          "```",
          "",
          "### 2. Windows：兼容环境 + LF 行尾 — **必做**",
          "",
          "用 Git Bash/WSL 兼容环境跑 hook；确认脚本行尾为 LF（避免 CRLF 执行错误）。",
          "",
          "### 3. 重装插件后还报错：先移除再重装 — **必做**",
          "",
          "```bash",
          "/plugin remove oh-my-claudecode",
          "```",
          "",
          "然后再重装。"
        ],
        "怎么确认修好了": [
          "",
          "hook 正常触发（预期形态）：",
          "",
          "```text",
          "Agent 会话提交/停止 → hook 日志正常输出，无 UserPromptSubmit/Stop hook error",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 权限 | chmod +x 后恢复（CSDN 2026-03 案例） |",
          "| Windows | Git Bash/WSL 兼容环境 + LF 行尾（Issue #30） |",
          "| 重装残留 | 先 remove 再装 |",
          "| 未实测项 | Claude Code 插件形态本市场未实测（无登录态） |",
          ""
        ],
        "症状": [
          "",
          "",
          "Agent 会话里 OMC 的 hook 不触发；Windows 上安装后报 `UserPromptSubmit/Stop hook error`（Issue #30 场景）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "1. hook 脚本无执行权限（`~/.claude/hooks/**/*.sh` 缺 +x）；",
          "2. Windows 下 shell 路径/转义差异，hook 脚本调用失败；",
          "3. 插件重装后旧 hook 残留。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 补执行权限：`chmod +x ~/.claude/hooks/**/*.sh`（`find ~/.claude/hooks -name '*.sh' -exec chmod +x {} +`）。",
          "2. Windows：用 Git Bash/WSL 兼容环境跑 hook；确认脚本行尾为 LF（避免 CRLF 执行错误）。",
          "3. 重装插件后如果还报错，先 `/plugin remove oh-my-claudecode` 再重装。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（钩子未执行 → chmod +x hooks，2026-03）；GitHub Issue #30（Windows hook error）。",
          "- 本市场未实测 Claude Code 插件形态（无登录态），如实标注。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-2",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-2"
      }
    },
    {
      "id": "oh-my-claudecode-3",
      "title": "智能体未自动委托（任务没被分发）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "配置了多 Agent，但会话里任务没有被自动委托/认领。多智能体配好了，却不干活。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 原因 | 说明 |",
          "|---|---|",
          "| CLAUDE.md 没加载 | 委托规则在 CLAUDE.md 里，Claude Code 没加载它 |",
          "| 插件装错目录 | 装到了别的项目目录，规则不在当前项目 |",
          "",
          "自动委托不是\"常驻进程\"，而是**会话启动时读一次规则**：Claude Code 启动时会按项目根目录加载 CLAUDE.md，把里面的委托规则（哪些任务交给哪个子 Agent）注册进会话。这个加载是**静默的**——失败了不会报错，只会表现为\"规则没生效\"。所以\"配了不干活\"的排查方向只有两个：规则文件在不在当前项目、内容有没有被识别。",
          "",
          "这也解释了为什么\"在别的目录跑过 setup\"等于没配：规则是按项目生成的，换目录就换了一套。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：在正确的项目根目录重跑 /omc-setup，再检查规则是否加载",
          "/omc-setup",
          "```",
          "",
          "在项目根目录执行（CLAUDE.md 按项目生成）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 检查 CLAUDE.md 是否加载 — **排查**",
          "",
          "会话里看 `/context` 是否包含 OMC 委托规则。",
          "",
          "### 2. 在正确的项目根目录重跑 /omc-setup — **必做**",
          "",
          "```bash",
          "/omc-setup",
          "```",
          "",
          "CLAUDE.md 按项目生成，必须在目标项目根目录跑。",
          "",
          "### 3. 确认委托规则写法 — **必做**",
          "",
          "与 v5 文档一致（guidance schema contract，见 AGENTS.md）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "会话上下文包含委托规则（预期形态）：",
          "",
          "```text",
          "/context → 输出包含 OMC 委托规则",
          "任务提交 → 自动委托/认领正常",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| CLAUDE.md 未加载 | 项目根目录重跑 /omc-setup（CSDN 2026-03） |",
          "| 插件装错目录 | 装到目标项目 |",
          "| 规则写法 | 与 v5 guidance schema 一致 |",
          "| 未实测项 | 插件形态需 Claude Code 会话，本市场未实测（CLI 形态纯命令面，） |",
          ""
        ],
        "症状": [
          "",
          "",
          "配置了多 Agent，但会话里任务没有被自动委托/认领。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "Claude Code 没加载 OMC 的 CLAUDE.md（委托规则在 CLAUDE.md 里）；或插件装到了别的项目目录。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 检查 CLAUDE.md 是否加载：会话里问/看 `/context` 是否包含 OMC 委托规则。",
          "2. 在正确的项目根目录重跑 `/omc-setup`（CLAUDE.md 按项目生成）。",
          "3. 确认委托规则写法与 v5 文档一致（guidance schema contract，见 AGENTS.md）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（智能体未自动委托 → CLAUDE.md 未加载检查，2026-03）。",
          "- 本市场实测：CLI 形态不依赖 CLAUDE.md（纯命令面，）；插件形态需 Claude Code 会话，未实测。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-3",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-3"
      }
    },
    {
      "id": "oh-my-claudecode-4",
      "title": "更新 / 配置异常（omc-doctor 诊断）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "升级后行为异常、配置冲突、命令报错。改不动、跑不动，也不知道哪里不对。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "版本间配置/迁移差异、旧配置残留——升级后旧配置和新版本对不上，行为就乱了。",
          "",
          "这类工具升级的\"行为异常\"几乎都是**配置 schema 漂移**：新版本改了配置的字段结构或语义（比如某条规则从\"全局生效\"改成\"按项目生效\"），而你磁盘上的旧配置还是老格式。工具不会自动迁移未知字段——能读的字段继续读，读不了的静默忽略，于是出现\"配置还在，但行为变了\"的诡异现象。",
          "",
          "`omc-doctor` 的价值就是把\"静默忽略\"变成\"显式报告\"：它逐项体检配置、版本、环境，把不匹配点直接列出来，省掉你逐条对比新老文档的功夫。",
          "",
          "所以升级后先跑诊断，比翻 changelog 快得多。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：跑内置诊断看问题点",
          "omc-doctor",
          "```",
          "",
          "按诊断结果修配置；保留输出再反馈问题（含版本号与 node 版本）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 跑 omc-doctor — **必做**",
          "",
          "```bash",
          "omc-doctor",
          "```",
          "",
          "内置诊断，直接指出问题点（CSDN 2026-04 案例路径）。",
          "",
          "### 2. 按诊断结果修配置 — **排查**",
          "",
          "必要时重装对齐版本：",
          "",
          "```bash",
          "npm i -g oh-my-claude-sisyphus@5.0.0",
          "```",
          "",
          "### 3. 反馈时保留诊断输出 — **排查**",
          "",
          "`omc-doctor` 输出含版本号与 node 版本，反馈问题时一并附上。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "诊断无异常（预期形态）：",
          "",
          "```text",
          "omc-doctor → 各检查项通过/无 ERROR",
          "```",
          "",
          "命令行为恢复正常。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 升级异常 | omc-doctor 定位 + 重装对齐版本 |",
          "| 配置冲突 | 按诊断结果修 |",
          "| 反馈问题 | 附 omc-doctor 输出（版本号 + node 版本） |",
          "| 未实测项 | omc-doctor 为配置面命令，需插件环境，本市场未实测（CLI 面） |",
          ""
        ],
        "症状": [
          "",
          "",
          "升级后行为异常、配置冲突、命令报错。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "版本间配置/迁移差异；旧配置残留。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 跑 `omc-doctor`（内置诊断）看问题点。",
          "2. 按诊断结果修配置；必要时 `npm i -g oh-my-claude-sisyphus@5.0.0` 重装对齐版本。",
          "3. 保留 `omc-doctor` 输出再反馈问题（含版本号与 node 版本）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（OMC 安装与实践全指南：更新/配置异常 → omc-doctor 诊断，2026-04）。",
          "- 本市场实测：CLI 面 `omc --help` 命令表含 launch 等；omc-doctor 为配置面命令，需插件环境未实测。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-4",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-4"
      }
    },
    {
      "id": "oh-my-claudecode-5",
      "title": "包名 / 插件名 / 命令名不一致",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "按插件名 \"oh-my-claudecode\" 去 npm 搜装不到；卸载时不知道包名。名字对不上，装也装不上、卸也不知道卸哪个。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "**三套名字不一致**：",
          "",
          "| 名字 | 用途 |",
          "|---|---|",
          "| `oh-my-claude-sisyphus` | npm 包名（安装/卸载用） |",
          "| `oh-my-claudecode` | Claude Code 插件名（插件操作用） |",
          "| `omc` | CLI 命令（日常用） |",
          "",
          "这不是\"命名混乱\"，是**三个层各有一个名字**，各有各的约束：npm 包名要全局唯一、不能和已有包冲突，所以项目方选了更长的 `oh-my-claude-sisyphus`（也反映了它受 Sisyphus 工作流启发）；Claude Code 插件名面向用户在插件市场里的认知，取 `oh-my-claudecode`；而 CLI 命令名要短、好敲，取 `omc`。",
          "",
          "三个名字服务于三个场景，互相之间没有自动映射——npm 只认包名，Claude Code 只认插件目录名，shell 只认命令名。",
          "",
          "所以\"按插件名去 npm 搜\"天然搜不到：你搜错了一个层。记住口诀：**装/卸认包名、插件操作认插件名、日常命令认 omc**。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：安装/卸载认准 npm 包名，日常用 omc",
          "npm i -g oh-my-claude-sisyphus && omc --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 安装/卸载用 npm 包名 — **必做**",
          "",
          "```bash",
          "npm i -g oh-my-claude-sisyphus",
          "```",
          "",
          "卸载：`npm rm -g oh-my-claude-sisyphus`。",
          "",
          "### 2. 日常使用命令 omc — **必做**",
          "",
          "```bash",
          "omc --version",
          "```",
          "",
          "### 3. 插件操作在 Claude Code 内用插件名 — **必做**",
          "",
          "Claude Code 内插件操作使用 `oh-my-claudecode`。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "版本命令正常（实测输出形态）：",
          "",
          "```text",
          "omc --version",
          "→ 5.0.0（manifest 闭环日志 step 1）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| npm 包名 | oh-my-claude-sisyphus（L33 记录） |",
          "| 插件名 | oh-my-claudecode |",
          "| CLI 命令 | omc |",
          "| Windows / macOS | 名字映射相同 |",
          ""
        ],
        "症状": [
          "",
          "",
          "按插件名 \"oh-my-claudecode\" 去 npm 搜装不到；卸载时不知道包名。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "三套名字不一致：npm 包 = `oh-my-claude-sisyphus`，Claude Code 插件 = `oh-my-claudecode`，CLI 命令 = `omc`。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 安装/卸载用 npm 包名：`npm i -g oh-my-claude-sisyphus` / `npm rm -g oh-my-claude-sisyphus`。",
          "2. 日常使用命令 `omc`；插件操作在 Claude Code 内用 `oh-my-claudecode`。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- L33 记录：包名与插件名不一致（oh-my-claude-sisyphus vs omc）。",
          "- 本市场实测：manifest install.cmd 用 npm 包名 + `omc --version` 验证（闭环日志 step 1：5.0.0）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-5",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-5"
      }
    },
    {
      "id": "oh-my-claudecode-6",
      "title": "Claude Code 本体环境类报错（OOM / Homebrew 冲突 / PowerShell 执行策略）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#6",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "安装 OMC 过程中 Claude Code 本体出问题：`OOM Killed`、Homebrew 与 npm 冲突、Windows 提示\"禁止运行脚本\"。你会以为 OMC 装坏了——其实**不是 OMC 的问题**，是 Claude Code 安装环境的常见坑。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 报错 | 根因 |",
          "|---|---|",
          "| OOM Killed | 内存不足（/omc-setup 依赖 Claude Code 本体） |",
          "| Homebrew 冲突 | brew 与 npm 双写冲突 |",
          "| 禁止运行脚本 | PowerShell 默认执行策略 |",
          "",
          "这一条的关键认知是**问题分层**：OMC 是 Claude Code 之上的封装层，`/omc-setup` 这类流程最终要调用 Claude Code 本体和系统包管理器——报错发生在哪一层，根因就在哪一层。",
          "",
          "三个报错分别对应三个不同的层：`OOM Killed` 是**资源层**（内存不足，任何进程都可能被杀，跟 OMC 无关）；Homebrew 冲突是**包管理层**（brew 和 npm 都写 bin 链接和全局包目录，双写同一位置互相覆盖）；“禁止运行脚本”是**策略层**（PowerShell 默认只允许签名脚本，未签名的 `.ps1` 一律拒绝执行）。",
          "",
          "判断方法固定：先看报错发生在哪个环节（OMC 自己的命令 vs Claude Code 本体 vs 系统工具），再按层找根因。",
          "",
          "这也解释了为什么 WSL 实测无此问题——Linux 没有 PowerShell 执行策略这一层。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：WSL/Linux 走 npm 路径装 CLI（避免 brew 双写）",
          "npm i -g oh-my-claude-sisyphus --registry=https://registry.npmmirror.com",
          "```",
          "",
          "Windows PowerShell 先放开执行策略：`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. OOM：加大内存/关闭多余进程 — **必做**",
          "",
          "`/omc-setup` 依赖 Claude Code 本体，内存不足就关多余进程或加内存后重试。",
          "",
          "### 2. Homebrew 冲突：统一 npm 路径 — **必做**",
          "",
          "统一用 npm 路径装 CLI（本市场路径 B），避免 brew 与 npm 双写：",
          "",
          "```bash",
          "npm i -g oh-my-claude-sisyphus",
          "```",
          "",
          "### 3. PowerShell：放开执行策略 — **必做**",
          "",
          "```powershell",
          "Set-ExecutionPolicy -Scope CurrentUser RemoteSigned",
          "```",
          "",
          "（按需）后再跑脚本。"
        ],
        "怎么确认修好了": [
          "",
          "安装与命令面正常（实测形态）：",
          "",
          "```text",
          "npm i -g oh-my-claude-sisyphus → 安装完成",
          "omc --version → 5.0.0（WSL npm CLI 形态实测无此问题）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| OOM | 加内存/关进程（CSDN 2026-03） |",
          "| Homebrew 冲突 | 统一 npm 路径 |",
          "| PowerShell | RemoteSigned 放开执行策略 |",
          "| WSL | 实测无此问题（闭环日志） |",
          ""
        ],
        "症状": [
          "",
          "",
          "安装 OMC 过程中 Claude Code 本体出问题：`OOM Killed`、Homebrew 与 npm 冲突、Windows 提示\"禁止运行脚本\"。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "不是 OMC 自身问题，是 Claude Code 安装环境的常见坑（内存不足、包管理器冲突、PowerShell 默认执行策略）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. OOM：加大内存/关闭多余进程后重试（`/omc-setup` 依赖 Claude Code 本体）。",
          "2. Homebrew 冲突：统一用 npm 路径装 CLI（本市场路径 B），避免 brew 与 npm 双写。",
          "3. PowerShell：`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`（按需）后再跑脚本。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（Claude Code 安装找不到命令/代理配置：OOM Killed/Homebrew 冲突；PowerShell 禁止运行脚本，2026-03；均为 Claude Code 本体弱相关）。",
          "- 本市场实测：WSL 环境 npm CLI 形态无此问题（闭环日志）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-6",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-6"
      }
    },
    {
      "id": "oh-my-claudecode-7",
      "title": "同名 fork 项目混淆",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "oh-my-claudecode"
      ],
      "evidence": "site docs#7;local repro log;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "搜 \"oh-my-claudecode\" 找到多个同名/近似项目，装错版本。教程来自不同 fork，命令对不上，装完发现不是你要的那个。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "上游存在**多个 fork/同名仓库**（如 AustinElite 等 fork 系），名字接近，搜索引擎容易混入。认准两个硬指标：原仓库 + npm 包名。",
          "",
          "| 指标 | 正确值 |",
          "|---|---|",
          "| 原仓库 | Yeachan-Heo/oh-my-claudecode |",
          "| npm 包名 | oh-my-claude-sisyphus |",
          "| 收录版本 | v5.0.0（2026-08-24 release） |",
          "",
          "GitHub 上“同名/近似名仓库”是**分叉生态的副产品**：任何开源项目都可以被 fork，fork 保留原名或近似名；搜索引擎把 fork、衍生项目和原仓库混在结果里，按热度排序，原仓库未必排第一。",
          "",
          "但**身份不随名字走**：判断是不是原仓库，要看两个与名字无关的硬指标——**组织/作者**（Yeachan-Heo，仓库的归属者）和**分发锚点**（npm 包名 `oh-my-claude-sisyphus`，安装时真正寻址的东西）。",
          "",
          "npm 包名尤其可靠：npm 上的名字是注册制的、唯一的，作者注册了它，安装命令指向的就是作者的包（除非包名也被人抢注，那属于另一种攻击）。所以验证顺序固定：先对 npm 包名（安装寻址一致）→ 再对原仓库（源码归属一致）→ 最后对版本号（内容一致）。三个都对上，就是原项目。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：装前看包信息，确认版本对齐",
          "npm view oh-my-claude-sisyphus version && npm i -g oh-my-claude-sisyphus@5.0.0 --registry=https://registry.npmmirror.com && omc --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 认准原仓库与包名 — **必做**",
          "",
          "原仓库 `Yeachan-Heo/oh-my-claudecode`；npm 包名 `oh-my-claude-sisyphus`（fork 系教程的安装命令不要用）。",
          "",
          "### 2. 版本对齐 v5.0.0 — **必做**",
          "",
          "```bash",
          "npm view oh-my-claude-sisyphus version",
          "```",
          "",
          "与收录版本 5.0.0（2026-08-24 release）对齐。",
          "",
          "### 3. 装完验证 — **必做**",
          "",
          "```bash",
          "omc --version",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "版本输出正确（实测输出形态）：",
          "",
          "```text",
          "omc --version",
          "→ 5.0.0（隔离 HOME 实测，149 包 added，exit 0）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| fork 同名 | 认准 Yeachan-Heo 原仓库（L32 信号） |",
          "| 版本对齐 | npm view 查最新，装前核对 |",
          "| 镜像安装 | npmmirror 实测 1m 装完 149 包 |",
          "| 卸载 | `npm rm -g oh-my-claude-sisyphus`，零残留（实测） |",
          ""
        ],
        "症状": [
          "",
          "",
          "搜 \"oh-my-claudecode\" 找到多个同名/近似项目，装错版本。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "上游存在多个 fork/同名仓库（如 AustinElite 等 fork 系），名字接近。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 认准原仓库：Yeachan-Heo/oh-my-claudecode；npm 包名 `oh-my-claude-sisyphus`。",
          "2. 版本对齐 v5.0.0（2026-08-24 release）；装前看包信息（`npm view oh-my-claude-sisyphus version`）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 国外信号（L32）：多 fork 同名（AustinElite 等）——名称混淆风险点名。",
          "- 本市场实测：收录签名 = Yeachan-Heo/oh-my-claudecode v5.0.0（manifest signature）。",
          ""
        ],
        "本市场实测安装 / 卸载（2026-08-28）": [
          "",
          "安装（manifest install.cmd，隔离 HOME=/tmp/l34-omc/home）：",
          "",
          "```",
          "[install] 执行: npm i -g oh-my-claude-sisyphus@5.0.0 --no-audit --no-fund --registry=https://registry.npmmirror.com && omc --version",
          "added 149 packages in 1m",
          "5.0.0",
          "[install] exit=0",
          "```",
          "",
          "卸载（manifest uninstall.cmd + verify）：",
          "",
          "```",
          "[uninstall] 执行: npm rm -g oh-my-claude-sisyphus",
          "removed 149 packages in 156ms",
          "[uninstall] 验证: omc removed: OK（fresh shell，verify exit=0）",
          "```",
          "",
          "- 完整日志：实测记录（本地留存）。",
          "- CLI 冒烟：实测记录（本地留存）（omc --version 5.0.0；usage 输出）。",
          "- 连通性：实测记录（本地留存）。",
          "- 未能实测：Claude Code 插件形态（/plugin install + /omc-setup 需登录会话）、MCP bridge 实跑、多 Agent 实排。",
          ""
        ],
        "冲突标注": [
          "",
          "- possible-conflict（推断态）：agent-governance-toolkit / ruflo / code-review-graph / cc-switch / claude-mem / skilldock——共享 ~/.claude.json 注册段 / Claude 插件注册面（详见矩阵对子证据）。",
          "- possible-compatible（推断态）：claude-code-router / deepcode / superpowers 等（CLI 面隔离，矩阵对子）。",
          "- 形态提醒：npm CLI 形态写面最小；Claude Code 插件形态写 ~/.claude 注册面，多插件共装时注意。",
          ""
        ],
        "名称混淆": [
          "",
          "- 三套名字：npm 包 oh-my-claude-sisyphus / 插件 oh-my-claudecode / 命令 omc（见症状 5）。",
          "- 同名 fork 项目（AustinElite 等）与上游 Yeachan-Heo 区分（见症状 7）。",
          ""
        ],
        "判据（验证状态）": [
          "",
          "- 症状清单：",
          "  - [x] 命令未找到 → 重跑 omc-setup（CSDN 2026-03；本市场 PATH 实测）",
          "  - [x] 钩子未执行 → chmod +x hooks（CSDN 2026-03；Issue #30 Windows hook error）",
          "  - [x] 智能体未自动委托 → CLAUDE.md 加载检查（CSDN 2026-03）",
          "  - [x] 更新/配置异常 → omc-doctor 诊断（CSDN 2026-04）",
          "  - [x] 包名/插件名/命令名不一致（L33 记录 + 本市场实测）",
          "  - [x] Claude Code 本体环境类：OOM/Homebrew/ExecutionPolicy（CSDN 弱相关）",
          "  - [x] 同名 fork 混淆（L32 国外信号）",
          "- 实测步骤：",
          "本站实测记录",
          "  - [x] 卸载验证（manifest uninstall.cmd：npm rm 149 包 → verify 零残留 exit 0）",
          "  - [x] 网络/镜像场景（npmmirror 1m 安装；）",
          "  - [x] 未能实测项：Claude Code 插件形态（/omc-setup 交互）、MCP bridge 实跑",
          ""
        ],
        "验收": [
          "",
          "- [x] 症状 → 原因 → 解法 → 验证证据 四段齐全（7 症状）",
          "- [x] 名称混淆已点名（三套名字不一致；fork 同名）",
          "- [x] check-words HARD 0",
          "- [x] validate 通过",
          "- [x] 未碰 GitHub / 未用任何账号"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-claudecode-7",
        "agent": "https://trove.xin/agent/problems/oh-my-claudecode-7"
      }
    },
    {
      "id": "oh-my-pi-1",
      "title": "安装成功但 omp --version 报 SyntaxError（旧版 bun）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "oh-my-pi"
      ],
      "evidence": "site docs#1;local repro log 2026-08-28",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`bun install -g @oh-my-pi/pi-coding-agent` 显示安装成功，但执行 `omp --version` 报 SyntaxError（或直接无声失败，无版本输出）。装上了却跑不了，而且报错还不提示版本不兼容。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "package.json engines 要求 **bun >=1.3.14**（本市场实测实读 engines 字段确认）；bun 1.3.11 及更早版本运行该包时解析新语法失败，报 SyntaxError 且不提示版本不兼容（来源：Qiita kai_kou，2026-08 检索；OrcaRouter 中文文档同标 bun >=1.3.14，相互印证）。",
          "",
          "`bun install -g` 只负责“把包装进全局目录”（安装期），不负责“装完后这个包能不能跑”（运行期）——它是**静态安装器**，不是运行时。装的是 JS 代码，跑的时候才由 `bun` 运行时逐行解析执行；运行时版本太旧，遇到包代码里的新语法（新语法糖/新 API）直接解析失败。",
          "",
          "SyntaxError 的诡异之处在于它不指向“版本”，只指向“第几行语法不认识”——如果包的入口文件用了新语法，老运行时在 cli.js 第一处新语法就崩，报错完全看不出是版本问题。所以“安装成功 ≠ 能运行”的检查顺序固定：`bun --version` 先验运行时（对照 engines 声明的版本线，如 >=1.3.14）→ 再 `omp --version` 验命令面。",
          "",
          "反过来，无声失败（无任何输出）也是同一根因——入口在解析阶段就崩了，连打印版本的机会都没有。",
          "",
          "| bun 版本 | omp --version 结果 |",
          "|---|---|",
          "| 1.3.11 及更早 | ❌ SyntaxError（实测：`SyntaxError: Unexpected identifier 'u'`，cli.js:159:1） |",
          "| ≥1.3.14（实测 1.4.0） | ✅ `omp/18.0.9` 正常 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：升级 bun 到 >=1.3.14，再验证版本",
          "curl -fsSL https://bun.sh/install | bash && bun --version && omp --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 升级 bun — **必做**",
          "",
          "```bash",
          "curl -fsSL https://bun.sh/install | bash",
          "```",
          "",
          "或包管理器升级；验证 `bun --version` ≥1.3.14。",
          "",
          "### 2. 重跑版本命令 — **必做**",
          "",
          "```bash",
          "omp --version",
          "```",
          "",
          "预期输出 `omp/<版本>`（实测 18.0.9）。"
        ],
        "怎么确认修好了": [
          "",
          "版本输出正常（实测输出形态）：",
          "",
          "```text",
          "omp --version",
          "→ omp/18.0.9（bun 1.4.0 实测）",
          "```",
          "",
          "升级前：bun 1.3.11 → `SyntaxError: Unexpected identifier 'u'`（实测复现，与 Qiita 症状一致）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| bun <1.3.14 | 升级 bun 后再跑 |",
          "| 包管理器升级 | brew/apt 等按 bun 官方方式 |",
          "| 无声失败 | 无版本输出 = 同样根因，先查 bun 版本 |",
          "| Windows | bun 安装器同样适用 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`bun install -g @oh-my-pi/pi-coding-agent` 显示安装成功，但执行 `omp --version` 报 SyntaxError（或直接无声失败，无版本输出）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "package.json engines 要求 `bun >=1.3.14`（本市场实测实读 engines 字段确认）；bun 1.3.11 及更早版本运行该包时解析新语法失败，报 SyntaxError 且不提示版本不兼容（来源：Qiita kai_kou，2026-08 检索；OrcaRouter 中文文档同标 bun >=1.3.14，相互印证）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 升级 bun 到 >=1.3.14：`curl -fsSL https://bun.sh/install | bash`（或包管理器升级），验证 `bun --version`。",
          "2. 升级后重跑 `omp --version` 确认输出 `omp/<版本>`。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28，bun 1.4.0）：本地项目安装 `@oh-my-pi/pi-coding-agent@18.0.9` 后 `omp --version` 输出 `omp/18.0.9`；package.json engines 实读为 {'bun': '>=1.3.14'}。",
          "- 旧 bun 1.3.11 复现（2026-08-28 补完）：bun 1.3.11 前置 PATH 执行 `omp --version` → `SyntaxError: Unexpected identifier 'u'`（cli.js:159:1），与 Qiita 症状一致；对照 bun 1.4.0 → `omp/18.0.9` 正常（实测复现日志（2026-08-28））。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-pi-1",
        "agent": "https://trove.xin/agent/problems/oh-my-pi-1"
      }
    },
    {
      "id": "oh-my-pi-2",
      "title": "命令混淆：omp install 与 omp plugin install",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "oh-my-pi"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "按网上教程执行 `omp install <插件名>` 失败或行为不符预期；正确姿势是 `omp plugin install <插件名>`。教程和实际命令对不上。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "npm 页面曾给出错误指令 `omp install`（来源：issue #36）。在 18.0.9 中 `omp install` 已被实现为 `omp plugin install` 的**别名**（帮助文本明示 alias of plugin install/plugin link），但旧文档/旧教程仍以错误姿势传播，新旧混用造成混淆。",
          "",
          "| 命令 | 状态 |",
          "|---|---|",
          "| `omp plugin install <name>` | ✅ 显式正确姿势 |",
          "| `omp install <name>` | ⚠️ 别名（帮助文本明示），旧教程仍按错误姿势传播 || `omp install <name>` | ⚠️ 别名（帮助文本明示），旧教程仍按错误姿势传播 |",
          "",
          "这是 CLI 命令面演进留下的\"文档化石\"：早期版本把安装命令设计成 `omp install`（issue #36 指出 npm 页面就这么写的），后来命令面改版成 `plugin` 子命令体系，为了不破坏老用户的肌肉记忆，把 `omp install` 保留成了别名。问题出在**教程的传播滞后**——写教程的人用的是旧版本，教程比命令面老，于是网上新旧两种姿势并存。",
          "",
          "`omp --help` 是命令面的真相源：它列出的子命令和别名标注（alias of plugin install/plugin link）是当前版本实打实的行为，比任何教程都新。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：显式使用子命令，最不易歧义",
          "omp plugin install <插件名>",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 显式使用 omp plugin install — **必做**",
          "",
          "```bash",
          "omp plugin install <name>",
          "```",
          "",
          "### 2. 不确定时先自查 — **必做**",
          "",
          "```bash",
          "omp --help",
          "```",
          "",
          "`install` / `plugin` 子命令均会列出（实测：`install` 标注 alias of plugin install/plugin link）。"
        ],
        "怎么确认修好了": [
          "",
          "帮助文本与命令行为正确（实测输出形态）：",
          "",
          "```text",
          "omp --help → 同时含 install（alias of plugin install/plugin link）与 plugin 子命令（实测）",
          "omp plugin install <name> → 正常安装",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 旧教程 | `omp install` 是错误姿势来源（issue #36） |",
          "| 18.0.9 | `omp install` 为别名，能用但易混淆 |",
          "| 不确定 | `omp --help` 自查子命令 |",
          ""
        ],
        "症状": [
          "",
          "",
          "用户按网上教程执行 `omp install <插件名>` 失败或行为不符预期；正确姿势是 `omp plugin install <插件名>`。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "npm 页面曾给出错误指令 `omp install`（来源：issue #36）；在 18.0.9 中 `omp install` 已被实现为 `omp plugin install` 的别名（帮助文本明示 alias of plugin install/plugin link），但旧文档/旧教程仍以错误姿势传播，新旧混用造成混淆。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 显式使用 `omp plugin install <name>`（最不易歧义）。",
          "2. 不确定时先 `omp --help` 自查子命令（install / plugin 均会列出）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28）：`omp --help` 输出同时含 `install`（标注 alias of plugin install/plugin link）与 `plugin` 子命令。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-pi-2",
        "agent": "https://trove.xin/agent/problems/oh-my-pi-2"
      }
    },
    {
      "id": "oh-my-pi-3",
      "title": "Windows/ARM release 二进制加载外部扩展失败",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "oh-my-pi"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "独立 omp 安装加载外部扩展报 `@oh-my-pi/pi-* is not host-provided`（Windows），或扩展加载静默失败（darwin-arm64）。典型求助样本：omp-compact#8（2026-08-28 demand 捕获）。装好主程序，扩展却加载不了。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "CHANGELOG v17.1.0 到 v17.1.5 期间的两个 bug：",
          "",
          "| 平台 | 问题 | 修复版本 |",
          "|---|---|---|",
          "| Windows | 编译二进制加载外部扩展失败（bare `@oh-my-pi/pi-*` 值导入问题） | v17.1.5 |",
          "| darwin-arm64 | release 二进制扩展静默失败（import.meta.dir 处理 bug） | v17.1.5 || darwin-arm64 | release 二进制扩展静默失败（import.meta.dir 处理 bug） | v17.1.5 |",
          "",
          "这类 bug 的共同根源是**编译型二进制和源码运行不是一回事**：`bun build --compile` 会把依赖打进单个可执行文件，模块解析规则随之改变——源码运行时，`import \"@oh-my-pi/pi-*\"` 能按 node_modules 解析；",
          "",
          "编译后 bare import 在宿主环境里找不到对应包（Windows 场景），`import.meta.dir` 指向的路径也和源码布局不同（darwin-arm64 场景）。",
          "",
          "这两处都不是用户配置能绕过的，属于上游打包产物缺陷，用户侧唯一可靠动作就是**升级到修复版本并确认版本号**——这也是为什么\"先 `omp --version` 再谈排查\"的顺序很重要。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：升级到 >=17.1.5（当前最新 18.0.9 已含修复）",
          "bun install -g @oh-my-pi/pi-coding-agent@latest",
          "```",
          "",
          "或按你的安装方式升级。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认当前版本 — **必做**",
          "",
          "```bash",
          "omp --version",
          "```",
          "",
          "### 2. 升级 — **必做**",
          "",
          "```bash",
          "bun install -g @oh-my-pi/pi-coding-agent@latest",
          "```",
          "",
          "### 3. 重试扩展加载 — **必做**",
          "",
          "升级后重装/重载外部扩展，确认不再报 `is not host-provided` 或静默失败。"
        ],
        "怎么确认修好了": [
          "",
          "升级后版本与扩展加载正常（预期形态）：",
          "",
          "```text",
          "omp --version → omp/18.0.9（≥17.1.5 即含修复）",
          "外部扩展加载 → 正常（不再报 not host-provided）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows | 报 `is not host-provided`，升级修复（CHANGELOG） |",
          "| darwin-arm64 | 静默失败，升级修复（import.meta.dir bug） |",
          "| Linux/WSL | 不在该症状范围（本市场环境） |",
          "| 未实测项 | Windows/ARM 二进制本环境未实测，按 CHANGELOG 整理 |",
          ""
        ],
        "症状": [
          "",
          "",
          "独立 omp 安装加载外部扩展报 `@oh-my-pi/pi-* is not host-provided`（Windows），或扩展加载静默失败（darwin-arm64）；典型求助样本：omp-compact#8（2026-08-28 demand 捕获）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "CHANGELOG v17.1.0 到 v17.1.5：Windows 编译二进制加载外部扩展失败（bare `@oh-my-pi/pi-*` 值导入问题）；darwin-arm64 release 二进制扩展静默失败（import.meta.dir 处理 bug）。均于 v17.1.5 修复。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "升级到 >=17.1.5（当前最新 18.0.9 已含修复）：`bun install -g @oh-my-pi/pi-coding-agent@latest`（或按你的安装方式升级）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材引用：CHANGELOG v17.1.0/v17.1.5 + demand 样本 omp-compact#8（维护者 检索核验）。",
          "- 本环境为 Linux/WSL，Windows/ARM 二进制未实测——如实标注；Linux 侧扩展加载不在该症状范围。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-pi-3",
        "agent": "https://trove.xin/agent/problems/oh-my-pi-3"
      }
    },
    {
      "id": "oh-my-pi-4",
      "title": "名称混淆：oh-my-pi（omp）vs pi-mono（pi）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "tool-identity",
      "category_label": "名称与来源混淆",
      "applies_to": [
        "oh-my-pi"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "搜索/阅读资料时把 oh-my-pi 与 pi-mono 混为一谈，安装后命令名对不上（pi vs omp）。教程里的命令和装出来的命令不是同一个。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 项目 | 仓库 | 命令名 |",
          "|---|---|---|",
          "| oh-my-pi | can1357/oh-my-pi（硬 fork） | `omp` |",
          "| pi-mono | 原项目 | `pi` |",
          "",
          "两者同源不同命令，文档与教程混用导致装错/查错。",
          ""
        ],
        "快速修复两者同源不同命令，文档与教程混用导致装错/查错。": [
          "",
          "oh-my-pi 是 pi-mono 的**硬 fork**：代码同源，但项目方改名发布，连 CLI 命令也换成 `omp`（npm 包 `@oh-my-pi/pi-coding-agent` 的 bin 字段就是 `{\"omp\": \"dist/cli.js\"}`，本市场实读确认）。",
          "",
          "命令名是**装完之后唯一可靠的辨识符**——文档可能过期、教程可能混用，但 `which omp` 装没装、`omp --version` 是哪个版本，机器不会骗你。",
          "",
          "所以\"认项目\"的正确顺序是：先看命令名，再回查仓库，而不是先信教程标题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：按命令名认项目——装完执行 omp --version 的是 oh-my-pi",
          "omp --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 按命令名认项目 — **必做**",
          "",
          "装完执行 `omp --version` 的是 oh-my-pi；`pi --version` 的是 pi-mono。",
          "",
          "### 2. 查文档认准仓库 — **必做**",
          "",
          "仓库 `can1357/oh-my-pi`（manifest 签名），勿混用 pi-mono 教程的安装命令。"
        ],
        "怎么确认修好了": [
          "",
          "版本输出与 bin 名正确（实测输出形态）：",
          "",
          "```text",
          "omp --version → omp/18.0.9（实测）",
          "package.json bin: {'omp': 'dist/cli.js'}（实测实读）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 装到 pi-mono | 命令名是 pi，与 omp 教程对不上 |",
          "| 教程混用 | 认准 can1357/oh-my-pi 仓库 |",
          "| 未实测项 | Windows/darwin-arm64 二进制、真实编码会话、VS Code 扩展形态 |",
          ""
        ],
        "症状": [
          "",
          "",
          "搜索/阅读资料时把 oh-my-pi 与 pi-mono 混为一谈，安装后命令名对不上（pi vs omp）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "oh-my-pi（can1357/oh-my-pi）是硬 fork，命令名为 `omp`；pi-mono 为原项目，命令名为 `pi`。两者同源不同命令，文档与教程混用导致装错/查错。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 按命令名认项目：装完执行 `omp --version` 的是 oh-my-pi；`pi --version` 的是 pi-mono。",
          "2. 查文档认准仓库：can1357/oh-my-pi（manifest 签名），勿混用 pi-mono 教程的安装命令。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测（2026-08-28）：安装包 `@oh-my-pi/pi-coding-agent` 的 bin 名实读为 `omp`（package.json bin: {'omp': 'dist/cli.js'}），`omp --version` 输出 `omp/18.0.9`。",
          ""
        ],
        "未能实测项（如实标注）": [
          "",
          "- Windows / darwin-arm64 二进制扩展（Linux/WSL 无对应资产）；真实编码会话（需 API key）；VS Code 扩展形态。",
          "- 环境机制发现：bun 1.4.0 的全局安装目录绑定 ~/.bun（设置 HOME 不生效），无法按手册 §5 隔离；本卡改用本地项目安装验证，系统全局已清理还原（详见反馈 oh-my-pi.@codex-ci.md）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/oh-my-pi-4",
        "agent": "https://trove.xin/agent/problems/oh-my-pi-4"
      }
    },
    {
      "id": "ollama-connect-failed",
      "title": "ollama 命令报 \"could not connect to a running Ollama instance\"（服务没启动）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "ollama"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "装完 ollama 跑 `ollama --version` / `ollama list`，输出带 `Warning: could not connect to a running Ollama instance`，你以为没装好或服务坏了。",
          "",
          "其实这是**提示服务没在运行**——ollama 是客户端 + 服务端一体，模型操作要先起服务（`ollama serve`，默认 127.0.0.1:11434）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`ollama` 命令默认连本机 11434 端口的服务；服务没启动时，命令仍能输出版本号但附加 `Warning: could not connect to a running Ollama instance`。需要 `ollama serve` 把服务拉起来（实测形态：Listening on 127.0.0.1:11434 (version 0.33.2)）。",
          "",
          "`ollama` 是「瘦客户端 + 独立服务」架构：命令本身只是客户端，真正的推理/拉取由 `ollama serve` 进程（监听 127.0.0.1:11434）承担。CLI 连接失败时**不把「连不上」当致命错误**，而是降级成警告继续输出版本信息——这是 CLI 的设计选择，也是「版本号能出、服务其实没跑」这个陷阱的来源。",
          "",
          "确定性推论：`ollama --version` 或 `ollama list` 里的 `Warning: could not connect to a running Ollama instance` 就是服务没起的信号，先 `pgrep -f \"ollama serve\"` 或 `ollama serve` 拉起；",
          "",
          "WSL/systemd 环境注意服务是后台进程，别在交互终端里 serve 完就退出；连不上优先查「服务进程在不在」，而不是查命令本身。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：启动服务（后台）+ 验证 API",
          "nohup ollama serve >/tmp/ollama.log 2>&1 & sleep 3 && curl -s http://127.0.0.1:11434/api/version",
          "```",
          "",
          "返回 `{\"version\":\"0.33.2\"}` 即服务就绪。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先分清是警告还是错误 — **必做**",
          "",
          "```bash",
          "ollama --version",
          "```",
          "",
          "能输出版本号 + 附带 Warning → 客户端正常，服务没起；直接下一步。",
          "",
          "### 2. 启动服务 — **必做**",
          "",
          "```bash",
          "ollama serve",
          "```",
          "",
          "前台运行（实测日志形态：server config 正常、Listening on 127.0.0.1:11434 (version 0.33.2)）。模型目录可用 `OLLAMA_MODELS` 指定（默认 `~/.ollama/models`，可指到大分区）。",
          "",
          "### 3. 后台运行时 — **必做**",
          "",
          "```bash",
          "nohup ollama serve >/tmp/ollama.log 2>&1 &",
          "```",
          "",
          "日志落 /tmp/ollama.log，后续排查看这里。",
          "",
          "### 4. 验证 API 与命令 — **必做**",
          "",
          "```bash",
          "curl -s http://127.0.0.1:11434/api/version && ollama list",
          "```",
          "",
          "API 返回版本 + `ollama list` 正常（空表 exit 0）即成功（实测）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "curl -s http://127.0.0.1:11434/api/version",
          "```",
          "",
          "返回 `{\"version\":\"0.33.2\"}` 即服务就绪。实测输出形态：Listening on 127.0.0.1:11434 (version 0.33.2)；`ollama list` 空表 exit 0。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 服务未启动 | 命令附 Warning，`ollama serve` 拉起即可（实测形态） |",
          "| 后台运行 | nohup + 日志文件，验证 /api/version |",
          "| OLLAMA_MODELS | 模型目录环境变量，可指到大分区（指南说明） |",
          "| OLLAMA_HOST | 改了监听地址/端口后，命令与客户端都要指向同一处；对外提供服务用 `OLLAMA_HOST=0.0.0.0:11434`（默认只绑 127.0.0.1，局域网/外部连不上）；旧版曾有回归只监听 localhost（ollama#12053），升版修复（本机 0.33.2 未复现回归，机制按官方标注） || OLLAMA_HOST | 改了监听地址/端口后，命令与客户端都要指向同一处（未实测） |",
          "| 端口占用 | 11434 被占时 serve 会报错，先查占用（未实测） |",
          ""
        ],
        "症状": [
          "",
          "`ollama --version` / `ollama list` 输出 `Warning: could not connect to a running Ollama instance`；模型操作不可用。",
          ""
        ],
        "原因": [
          "",
          "ollama 命令是客户端 + 服务端一体：服务（`ollama serve`，默认 127.0.0.1:11434）没启动时，命令仍能输出版本但提示连不上实例。",
          ""
        ],
        "解法": [
          "",
          "1. `ollama serve` 启动服务（前台或 nohup 后台）。",
          "2. `curl -s http://127.0.0.1:11434/api/version` 验证。",
          "3. `ollama list` 确认命令面恢复。",
          ""
        ],
        "验证证据": [
          "",
          "- 实测（2026-08-29，WSL Ubuntu，ollama 0.33.2）：`ollama serve` 启动日志正常（ssh key 生成、blobs: 0、Listening on 127.0.0.1:11434 (version 0.33.2)），`/api/version` 返回 `{\"version\":\"0.33.2\"}`，`ollama list` 空表 exit 0（实测记录（本地留存）、）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **同域（相邻问题）**：",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（同域：下载链路）",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Open WebUI 页面显示连不上 Ollama](open-webui-ollama-connect.html)（WebUI 连不上 ollama 服务时的排查）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ollama-connect-failed",
        "agent": "https://trove.xin/agent/problems/ollama-connect-failed"
      }
    },
    {
      "id": "ollama-install-slow",
      "title": "ollama 装不上/官方安装脚本国内拉不动（下载慢、超时）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "ollama"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "跑官方 `install.sh`（或从官网下载安装包）时卡住/超时，几分钟不见进度；或下载到一半断掉。你以为是网络整体挂了——其实官方下载源国内直连不稳，**换 GitHub Release 加速通道即可**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "官方安装脚本从 ollama.com 拉取安装包，该域名国内直连不稳（慢/超时）。而 ollama 的安装包本体同时发布在 GitHub Release（`ollama-linux-amd64.tar.zst`），GitHub 可通过 gh-proxy 类加速站稳定拉取（实测 1.42GB 通过）。",
          "",
          "官方安装脚本（`curl ... | sh`）的本质是「下载器 + 解包器」：它先从 ollama.com 拉安装包（国内直连不稳定 → 慢/超时），再解包到 /usr/local。关键点：**同一个安装包本体同时发布在 GitHub Release**（`ollama-linux-amd64.tar.zst`），资产是同一份，只是下载通道不同——换通道不换资产。",
          "",
          "gh-proxy 类加速站做的是「代理转发 GitHub Release 下载请求」，把国内直连不稳定的 `objects.githubusercontent.com` 换成可达的代理域名。",
          "",
          "确定性推论：`curl -I` 分别测 ollama.com 与 gh-proxy 通道即可定位瓶颈在哪个域名；下载后 `tar --zstd -xf` + 手动放 PATH 与官方脚本产物等价（都只是解包）；先确认下载完成再解包，别在下载阶段反复重试同一通道。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：gh-proxy 拉 GitHub Release tar.zst → 解压到 /usr/local → 验证版本",
          "curl -fsSL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst && tar --zstd -xf /tmp/ollama.tar.zst -C /usr/local && ollama --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是下载通道问题 — **必做**",
          "",
          "```bash",
          "curl -sI --max-time 10 https://ollama.com/install.sh",
          "```",
          "",
          "超时/000 → 官方源不可达；走 gh-proxy Release 路径。",
          "",
          "### 2. gh-proxy 下载 Release 包 — **必做**",
          "",
          "```bash",
          "curl -fsSL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst",
          "```",
          "",
          "实测：1.42GB（1422262024 bytes，v0.33.2，2026-08-29）。下载量大，网络波动时用 `curl -C -` 断点续传。",
          "",
          "### 3. 解压（需要 zstd 工具） — **必做**",
          "",
          "```bash",
          "tar --zstd -xf /tmp/ollama.tar.zst -C /usr/local",
          "```",
          "",
          "报 `tar: unrecognized option --zstd` 先装：`apt-get install -y zstd`（实测 zstd 1.5.7）。",
          "",
          "### 4. 验证 — **必做**",
          "",
          "```bash",
          "ollama --version",
          "```",
          "",
          "输出 `ollama version 0.33.2` 即成功（实测形态：客户端版本 0.33.2）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ollama --version && ollama list",
          "```",
          "",
          "版本号正常 + `ollama list` 能跑（空表 exit 0）即成功（实测：）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 官方 install.sh | ollama.com 直连不稳（推断），换 gh-proxy Release |",
          "| 断点续传 | 大文件用 `curl -C -` 重试 |",
          "| 缺 zstd | `tar: unrecognized option --zstd` → `apt-get install -y zstd`（实测） |",
          "| Windows | 用官方安装器（建议 WSL2 内与 Linux 同路径） |",
          "| macOS | 官方 dmg（Apple Silicon 原生） |",
          "| 加速站生命周期 | gh-proxy 类加速站有生命周期，失效时换可达镜像 |",
          ""
        ],
        "症状": [
          "",
          "官方 `install.sh` / 官网下载卡住、超时或中途断掉；ollama 装不上。",
          ""
        ],
        "原因": [
          "",
          "官方下载源（ollama.com）国内直连不稳定；安装包本体在 GitHub Release 可通过 gh-proxy 加速拉取。",
          ""
        ],
        "解法": [
          "",
          "1. `curl -fsSL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst`。",
          "2. `tar --zstd -xf /tmp/ollama.tar.zst -C /usr/local`（缺 zstd 先 `apt-get install -y zstd`）。",
          "3. `ollama --version` 验证。",
          ""
        ],
        "验证证据": [
          "",
          "- 实测（2026-08-29，WSL Ubuntu，codex-ci）：gh-proxy 拉取 ollama-linux-amd64.tar.zst（1422262024 bytes = 1.42GB，v0.33.2），tar --zstd（zstd 1.5.7）解压到隔离目录成功，`ollama --version` 输出客户端版本 0.33.2，`ollama list` exit 0（实测复现日志（2026-08-29））。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（装好后的下载链路）",
          "  - [模型目录换盘](ollama-models-migration.html)（默认模型目录与安装方式相关）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ollama-install-slow",
        "agent": "https://trove.xin/agent/problems/ollama-install-slow"
      }
    },
    {
      "id": "ollama-models-migration",
      "title": "模型都下到 C 盘/系统盘了，怎么把 Ollama 模型目录换到别的盘",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "ollama",
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "本地容器实测（ollama/ollama:latest）+ snap 配置机制核对（snap_launcher.sh）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Ollama 装好之后一直用默认目录存模型：Linux 是 `~/.ollama/models`，Windows 是 `C:\\Users\\你\\.ollama\\models`，Snap 安装是 `/var/snap/ollama/common/models`。模型一个比一个大（7B 模型动辄 4~5GB），系统盘很快被塞满。",
          "",
          "你想把模型放到 D 盘/别的挂载盘，但不知道改哪里——网上说法杂：有人说改 `OLLAMA_MODELS`，有人说把整个 `.ollama` 目录搬走，还有人说必须重装。",
          "",
          "其实机制很简单：**ollama 读哪个目录完全由 `OLLAMA_MODELS` 环境变量决定**，换目录 = 搬目录 + 改环境变量，不用重装、不用重新下载。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "1. ollama 每次启动时用 `OLLAMA_MODELS` 决定模型存放目录；**没设这个变量**时，才用平台默认目录（Linux/macOS `~/.ollama/models`、Windows `%USERPROFILE%\\.ollama\\models`、snap `/var/snap/ollama/common/models`）。",
          "2. 模型文件 = `manifests/`（索引）+ `blobs/`（真正的权重文件），两者都在 models 目录里，整个目录搬走即可，不依赖绝对路径。",
          "3. 常见误区：直接把模型目录 `mv` 走而不改环境变量 → ollama 又按默认目录建了个空目录，表现为\"模型全没了\"；其实模型还在原地，改回环境变量或把目录放回去就恢复。",
          "4. 注意区分：`OLLAMA_MODELS` 是**模型**目录；`OLLAMA_HOST` 是**服务**地址。Open WebUI 连不上时改的是后者（见 open-webui-ollama-connect），别混。",
          "",
          "模型目录解析是「环境变量优先，默认值兜底」：ollama **每次启动时**读 `OLLAMA_MODELS` 决定 models 目录，没设才用平台默认（Linux `~/.ollama/models`、Windows `%USERPROFILE%\\.ollama\\models`、snap `/var/snap/ollama/common/models`）。",
          "",
          "models 目录内部是 `manifests/`（模型索引，记录层与 digest）+ `blobs/`（权重文件本体），目录整体搬走即可——两个子目录之间没有绝对路径依赖。",
          "",
          "陷阱来自「解析时点」：直接把目录 `mv` 走但没改环境变量，下次启动又按默认目录建了空目录，看起来「模型全没了」，其实模型还在原地。",
          "",
          "确定性推论：迁移三步=设 `OLLAMA_MODELS` 指向新目录 → 搬 manifests+blobs → **重启服务**（变量只在启动时读）；`OLLAMA_MODELS` 与 `OLLAMA_HOST` 是两个不同变量（模型目录 vs 服务地址），改错对象等于没改。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 假设新目录是 /data/ollama-models（先建好）",
          "# 1) 停服务",
          "sudo systemctl stop ollama            # systemd 安装",
          "# snap 安装: sudo systemctl stop snap.ollama.service",
          "",
          "# 2) 搬目录（首次是复制，确认没问题再删旧的；mv 更快但要承担风险）",
          "sudo mv ~/.ollama/models /data/ollama-models",
          "# snap 安装: sudo mv /var/snap/ollama/common/models /data/ollama-models",
          "",
          "# 3) 告诉 ollama 新目录",
          "# systemd 安装: 编辑 /etc/systemd/system/ollama.service 的 [Service] 加一行",
          "#   Environment=\"OLLAMA_MODELS=/data/ollama-models\"",
          "sudo systemctl daemon-reload",
          "# Linux 标准安装器（deb/rpm，服务以 ollama 用户运行）：新目录必须先给 ollama 用户读写权限",
          "sudo chown -R ollama:ollama /data/ollama-models",
          "",
          "# 4) 起服务 + 验证",
          "sudo systemctl start ollama",
          "ollama list      # 模型应该原样还在",
          "```",
          "",
          "- **Snap 安装**：改法不同（不是编辑 service 文件），见下面\"一步步来\"第 3 步：`sudo snap set ollama models=/data/ollama-models`。",
          "- **Docker 安装**：启动参数里加 `-e OLLAMA_MODELS=/models -v /data/ollama-models:/models`（把宿主机新目录挂进容器）。",
          "- **Windows 直装**：系统环境变量里新增用户变量 `OLLAMA_MODELS=D:\\ollama-models`，重启 ollama（托盘退出重开）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看当前模型目录在哪、多大 — **必做**",
          "",
          "```bash",
          "ollama list                    # 有哪些模型",
          "# Linux 默认 ~/.ollama/models；snap 是 /var/snap/ollama/common/models",
          "du -sh ~/.ollama/models        # 看占了多少",
          "```",
          "",
          "### 2. 停服务（别在跑的时候搬） — **必做**",
          "",
          "```bash",
          "sudo systemctl stop ollama            # curl 脚本安装",
          "sudo systemctl stop snap.ollama.service   # snap 安装",
          "```",
          "",
          "`pgrep -af ollama` 应没有 `serve` 进程了（剩 launcher 也不影响）。",
          "",
          "### 3. 按安装方式设置新目录 — **必做**",
          "",
          "**systemd（官方脚本安装）**：编辑 `/etc/systemd/system/ollama.service`，在 `[Service]` 段加：",
          "",
          "```ini",
          "Environment=\"OLLAMA_MODELS=/data/ollama-models\"",
          "```",
          "",
          "然后 `sudo systemctl daemon-reload`。",
          "",
          "**Linux 标准安装器（deb/rpm）与 curl 脚本不一样**：标准安装器默认目录是 `/usr/share/ollama/.ollama/models`，服务以 `ollama` 用户运行——新目录必须 `sudo chown -R ollama:ollama <新目录>`，否则 ollama 没有写权限、模型会\"消失\"（官方维护者在 GitHub issue #9677 中确认）；",
          "",
          "curl 脚本安装才是当前用户的 `~/.ollama/models`。",
          "",
          "**snap 安装（本机实测过配置机制）**：snap 的启动脚本读 `snapctl get models`，所以：",
          "",
          "```bash",
          "sudo snap set ollama models=/data/ollama-models",
          "sudo systemctl restart snap.ollama.service",
          "```",
          "",
          "**Docker 安装**：容器加挂载 + 环境变量：",
          "",
          "```bash",
          "docker run -d -v /data/ollama-models:/models \\",
          "  -e OLLAMA_MODELS=/models \\",
          "  -p 11434:11434 ollama/ollama",
          "```",
          "",
          "**Windows 直装**：`设置 → 系统 → 关于 → 高级系统设置 → 环境变量`，新建用户变量 `OLLAMA_MODELS=D:\\ollama-models`，然后退出托盘 ollama 重新打开。",
          "",
          "### 4. 搬目录 — **必做**",
          "",
          "```bash",
          "# 保险做法：先复制验证，确认无误再删旧的（模型大，复制慢但可回退）",
          "sudo cp -a ~/.ollama/models /data/ollama-models",
          "# 或确认无误后直接 mv",
          "sudo mv ~/.ollama/models /data/ollama-models",
          "```",
          "",
          "### 5. 起服务 + 验证 — **必做**",
          "",
          "```bash",
          "sudo systemctl start ollama",
          "ollama list                     # 模型必须原样还在（名字/大小一致）",
          "ollama run <之前有的模型> \"hi\"   # 能跑才算数",
          "```",
          "",
          "### 6. 确认新下载也落新目录 — **必做**",
          "",
          "```bash",
          "ollama pull <一个小模型>        # 比如 all-minilm:l6-v2（45MB）",
          "ls /data/ollama-models/blobs    # 新文件出现在这里",
          "```"
        ],
        "怎么确认修好了": [
          "",
          "- `ollama list` 和迁移前完全一致（同样的模型名、大小）。",
          "- 随便 `ollama run` 一个旧模型能正常出字——不是只读到了 manifests，权重也能加载。",
          "- 新 `ollama pull` 后，新目录 `blobs/` 里出现新文件；旧目录 `blobs/` 数量不再增长。",
          "- 验证旧目录不再被读：把旧目录临时改名后 `ollama list` 依然正常（改错环境变量时，这里会变成\"模型全没了\"）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- **Linux curl 脚本安装**：默认 `~/.ollama/models`，systemd 单元 `/etc/systemd/system/ollama.service` 加 `Environment`。",
          "- **Snap 安装**：默认 `/var/snap/ollama/common/models`，配置走 `snap set ollama models=...`（启动脚本 `snap_launcher.sh` 里 `OLLAMA_MODELS=$(snapctl get models)`），不是编辑 service 文件。",
          "- **Docker**：`-e OLLAMA_MODELS=/models` + 卷挂载；卷名随意，关键是要把宿主机目录挂到环境变量指向的容器路径。",
          "- **macOS 直装**：`launchctl setenv OLLAMA_MODELS /Volumes/xxx/ollama-models`（或图形界面 app 的默认 `~/.ollama/models`）。",
          "- **Windows**：用户环境变量 `OLLAMA_MODELS`，改完要退出托盘 ollama 重新打开才生效。官方 GUI 路径：设置（Win11）/ 控制面板（Win10）搜索\"环境变量\"→ \"编辑账户的环境变量\" → 新建/编辑用户变量 `OLLAMA_MODELS=D:\\ollama-models`（docs.ollama.com/windows 原文）。注意：新版托盘 GUI 里改模型路径与命令行读到的不一致（GitHub issue #12154），**一律以环境变量为准**；官方还说明卸载 Ollama 不会删除 `OLLAMA_MODELS` 指向的模型。",
          "- **Open WebUI 用户**：换模型目录不影响 Open WebUI 连接（它连的是 `OLLAMA_HOST`/`OLLAMA_BASE_URL`），只要 ollama 重启后 `ollama list` 正常，WebUI 里模型照常出现。",
          ""
        ],
        "症状": [
          "",
          "- 系统盘满了，想给模型换目录但 `mv` 之后 `ollama list` 变空。",
          "- 找遍教程不知道 snap / docker / Windows 各自怎么改。",
          "- 换完目录新下载的模型还是出现在旧路径。",
          ""
        ],
        "原因": [
          "",
          "- `OLLAMA_MODELS` 未设置时用平台默认目录；只搬目录不改环境变量，ollama 会按默认目录重建空目录，表现为模型丢失（实际没丢）。不同安装方式（systemd/snap/docker/Windows）配置环境变量的入口不同。",
          ""
        ],
        "解法": [
          "",
          "- 停服务 → 复制/移动 models 目录到新位置 → 按安装方式设置 `OLLAMA_MODELS`（systemd 加 Environment；snap `snap set ollama models=`；docker `-e`；Windows 用户环境变量）→ 重启 → `ollama list` + 实际运行验证 → pull 一个小模型确认新下载落新目录。",
          ""
        ],
        "验证证据": [
          "",
          "- 实测（ollama/ollama:latest 独立容器，不动本机 snap 实例）：",
          "  - 复制本机模型（qwen2.5-0.5b，469M）到新目录 `/tmp/ollama-models`；",
          "  - 容器 A 设 `OLLAMA_MODELS=/models`（挂载新目录）→ `ollama list` 直接显示 qwen2.5-0.5b 491MB（未重新下载）；",
          "  - 容器 B 不设 `OLLAMA_MODELS` → 默认 `~/.ollama/models` 存在但为空，`ollama list` 为空（对照）；",
          "  - `ollama show qwen2.5-0.5b` 正常；`ollama run qwen2.5-0.5b \"say hi\"` CPU 正常出字；",
          "  - `ollama pull all-minilm:l6-v2` → success，新目录 blobs 2→6，`ollama list` 同时显示两个模型；",
          "  - 本机 snap 配置机制核对：`/snap/ollama/current/bin/snap_launcher.sh` 中 `OLLAMA_MODELS=$(snapctl get models)`，`snap get ollama` 有 models 键 → snap 改目录用 `sudo snap set ollama models=<路径>`；",
          "  - 测试容器与临时目录已清理。",
          "- 完整日志：`实测复现日志（2026-08-30）`",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [RAG 知识库检索不生效 / embedding 配置错](open-webui-rag-embedding.html)（embedding 模型也在模型目录里，一起搬）",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（新下载会落到新目录；拉取慢另见）",
          "  - [ollama 装不上/官方脚本拉不动](ollama-install-slow.html)（安装方式决定默认目录与配置入口）",
          ""
        ],
        "参考资料": [
          "",
          "- Ollama 官方文档（Windows：安装位置 / 模型目录 / 环境变量）— <https://docs.ollama.com/windows>",
          "- GitHub issue [ollama/ollama#9677](https://github.com/ollama/ollama/issues/9677)（Linux 标准安装器需 chown ollama:ollama）— <https://github.com/ollama/ollama/issues/9677>",
          "- GitHub issue [ollama/ollama#12154](https://github.com/ollama/ollama/issues/12154)（GUI 改路径与 CLI 不一致，以环境变量为准）— <https://github.com/ollama/ollama/issues/12154>"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ollama-models-migration",
        "agent": "https://trove.xin/agent/problems/ollama-models-migration"
      }
    },
    {
      "id": "ollama-pull-timeout",
      "title": "ollama pull 卡住/超时（官方 registry 国内直连不稳定）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "ollama"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "装好 ollama 后跑 `ollama pull qwen2.5`，转圈几十分钟没动静，或直接报连接超时；模型一个都拉不下来。你以为是命令写错或模型不存在——其实是**官方模型 registry 国内直连不稳定**，不是命令问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "ollama 默认从官方 registry（ollama.com / registry.ollama.ai）拉模型，国内网络直连该域名不稳定（实测：官方 registry 直连超时）。模型文件单个 4~70GB，传输中途断掉重来更难受。",
          "",
          "**正确路径（本市场实测）**：从 ModelScope（国内可达）下载 GGUF 文件，再用 `ollama create` 导入——绕开官方 registry。",
          "",
          "2026-08-29 实测：qwen2.5-0.5b-instruct-q4_k_m GGUF 491MB 下载 58.5s + create 导入成功 + CPU 冒烟通过（证据：实测复现日志（2026-08-29））。",
          "",
          "`ollama pull` 的链路是「CLI → registry 寻址（ollama.com/registry.ollama.ai）→ 按 digest 拉取各层 blob → 写入 blobs/ 并生成 manifests/ 索引」——registry 域名直连不稳时，连接在传输层就失败；模型单文件 4~70GB，中途断掉要从头重来。",
          "",
          "GGUF 导入路径绕开的正是「registry 寻址」这一环：`ollama create` 直接消费本地 GGUF 文件，计算 digest、写入 blobs/、生成 manifest——**下载环节换成国内可达的 ModelScope，导入环节不碰网络**。",
          "",
          "确定性推论：`ollama pull` 卡住先确认是 registry 域名问题（`curl -I https://registry.ollama.ai`）而不是命令问题；GGUF 导入后 `ollama list` 出现即成功，模型与 pull 来的在运行层面等价（都是 manifest+blob）；",
          "",
          "后续 `ollama run` 不再需要网络，离线可用。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：ModelScope 下载 GGUF → ollama create 导入（模型名示例 qwen2.5:0.5b）",
          "curl -sL -o /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf && ollama create qwen2.5:0.5b -f - <<EOF",
          "FROM /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf",
          "EOF",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是网络不是命令 — **必做**（先排除服务/命令问题）",
          "",
          "```bash",
          "ollama list",
          "```",
          "",
          "服务正常但 pull 卡住/超时 → 网络问题；换 ModelScope 路径。",
          "",
          "### 2. 从 ModelScope 下载 GGUF — **必做**（下载前先 `df -h /` 确认磁盘）",
          "",
          "```bash",
          "curl -sL -o /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf",
          "```",
          "",
          "实测：491MB / 58.5s（2026-08-29，校园网可达）。下载前先确认磁盘够：`df -h /`。",
          "",
          "### 3. 用 ollama create 导入 — **必做**",
          "",
          "```bash",
          "ollama create qwen2.5:0.5b -f - <<EOF",
          "FROM /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf",
          "EOF",
          "```",
          "",
          "（Modelfile 也可写成文件再 `-f ./Modelfile`。）",
          "",
          "### 4. 验证并运行 — **必做**（验收）",
          "",
          "```bash",
          "ollama list && ollama run qwen2.5:0.5b \"你好\"",
          "```",
          "",
          "能列出模型、能出回答即成功（实测：create success + CPU 冒烟通过）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ollama list",
          "```",
          "",
          "列表出现导入的模型即成功。实测输出形态：ModelScope 下载 491MB/58.5s → `ollama create` success → 模型出现在列表。",
          ""
        ],
        "什么时候不需要这个解法": [
          "",
          "这个解法解决的是「官方 registry 国内直连不稳」；如果你的网络能直连，直接用 `ollama pull` 更省事，不用绕 GGUF 导入。",
          "",
          "- **能直连官方 registry 时**：`curl -I https://registry.ollama.ai` 通（返回 200/301）——直接 `ollama pull`，GGUF 导入是绕路。",
          "- **要精确版本组合时**：GGUF 导入需要自己找对应文件，官方 pull 的 digest 映射更可靠；没有镜像源问题就别换路径。",
          "- **已在容器里跑 ollama 时**：容器镜像通常内置 registry 配置，先试镜像内 pull，比在宿主机 GGUF 导入快。",
          "",
          "不确定？先跑 `curl -I https://registry.ollama.ai`——通就走官方，不通再走本文路径。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 官方 registry 直连 | 实测超时，别硬等 |",
          "| ModelScope GGUF | 实测 491MB/58.5s 可用（qwen2.5-0.5b-instruct-q4_k_m） |",
          "| HF 镜像 | hf-mirror.com 等可达时同样可下 GGUF 再导入（未实测） |",
          "| 磁盘 | 模型 4~70GB，先 `df -h /` 确认 |",
          "| LLaMA-Factory 微调 | 模型获取可走同路径（ModelScope GGUF），微调组合见 combo-local-llm-finetune |",
          "| 代理设置 | 用户环境变量 http_proxy 对 ollama 下载可能不生效（ollama#15358 外部报告 ignores http_proxy，未本地复现）；代理需注入 ollama serve 进程（systemd `Environment=\"HTTPS_PROXY=...\"`） |",
          "| 网络抖动中途断掉 | 传输中断后直接重跑 `ollama pull`：已下载的分片写入 blobs/ 会复用、从断点继续（机制核对，未实测）；多次重试仍断再换 ModelScope GGUF 路径 |",
          "| 模型名不存在 | 报 `pull model manifest: ... not found`——是模型名/标签拼写或不存在，不是网络问题；先到 ModelScope / HuggingFace 核对准确模型名 |",
          "| LLaMA-Factory 微调 | 模型获取可走同路径（ModelScope GGUF），微调组合见 combo-local-llm-finetune |",
          ""
        ],
        "症状": [
          "",
          "`ollama pull` 失败的常见形态：",
          "",
          "```",
          "# 形态 1：卡住无进度 / 连接超时（registry 直连不稳）",
          "ollama pull qwen2.5",
          "# 转圈几十分钟没动静，或 Error: ... dial tcp ... i/o timeout",
          "",
          "# 形态 2：大文件传输中途断掉",
          "# 下载到一半报 unexpected EOF / connection reset",
          "",
          "# 形态 3：模型名不存在（不是网络问题）",
          "Error: pull model manifest: ... not found",
          "```",
          "",
          "同族但根因不同（别在本页死磕网络）：",
          "",
          "- `pull model manifest: ... not found` → 模型名/标签拼写错或不存在，先到 ModelScope / HuggingFace 核对名称；",
          "- `no space left on device`（下载/导入阶段）→ 模型 4~70GB，先 `df -h /` 确认磁盘；WSL 下 df 有空间但写不进是宿主盘满，见 [wsl-no-space-left](wsl-no-space-left.html)。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "官方模型 registry（ollama.com / registry.ollama.ai）国内直连不稳定，大模型文件传输中途失败。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 从 ModelScope 下载 GGUF：`curl -sL -o /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/master/qwen2.5-0.5b-instruct-q4_k_m.gguf`。",
          "2. `ollama create qwen2.5:0.5b -f - <<EOF",
          "FROM /tmp/qwen2.5-0.5b-instruct-q4_k_m.gguf",
          "EOF` 导入。",
          "3. `ollama list` 确认，`ollama run` 验证。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 实测（2026-08-29，WSL Ubuntu，ollama 0.33.2 隔离环境）：ModelScope 下载 qwen2.5-0.5b-instruct-q4_k_m GGUF（491MB，58.5s）→ `ollama create` success → CPU 冒烟通过（实测复现日志（2026-08-29））。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 本页 → 同根 / 相关」读：",
          "",
          "- **前置（先排除）**：[ollama 装不上/官方脚本拉不动](ollama-install-slow.html)——装不上就没有 pull 这回事",
          "- **后置（拉完之后的坑）**：[ollama 报 could not connect](ollama-connect-failed.html)——模型拉完但服务没起/连不上",
          "- **同根（registry 直连不稳 → 换源）**：[docker pull 失败/镜像加速](docker-hub-pull-fail.html)（docker 镜像 registry 直连不稳 → 镜像加速器，同族解法）",
          "- **相关（同域延展）**：",
          "  - [RAG 知识库检索不生效 / embedding 配置错](open-webui-rag-embedding.html)（embedding 模型拉不下来 → 上传文件报 404）",
          "  - [模型目录换盘](ollama-models-migration.html)（模型落盘目录可迁移）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ollama-pull-timeout",
        "agent": "https://trove.xin/agent/problems/ollama-pull-timeout"
      }
    },
    {
      "id": "open-webui-deploy-fail",
      "title": "Open WebUI 用 Docker 起不来（镜像拉不动、端口被占、页面打不开、数据丢）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "open-webui",
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-09-24)",
      "source": "站内文档（工具侧 2026-09-01 实测素材，本条目 2026-09-24 独立复现）",
      "recorded": "2026-09-24",
      "sections": {
        "你会遇到什么": [
          "",
          "你照着教程在 Docker 里部署 Open WebUI：`docker pull ghcr.io/open-webui/open-webui` 拉到一半转圈直到超时；或者容器终于起来了，浏览器打开 `localhost:3000` 却连接被拒、一直转圈；或者报 `port is already allocated` 根本创建不了容器。",
          "",
          "还有一个更隐蔽的：能用了几天，删掉容器重建，账号、历史会话、知识库全没了。",
          "",
          "四类表现（镜像拉不动 / 端口冲突 / 页面打不开 / 数据丢）根因各不相同，下面逐条拆。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先说结论：这四条路各自有一个固定断点——镜像走错源、宿主端口被占、启动需要等 2-3 分钟、数据没挂卷。",
          "",
          "**断点 1：镜像源。** Open WebUI 官方镜像在 ghcr.io，国内直连拉取不稳定。本机实测（2026-09-24，WSL2 Ubuntu + Docker 29.1.3）：`ghcr.io` 直连在 25 秒限时内没拉完（只完成了部分层，进程超时退出）；换南京大学镜像 `ghcr.nju.edu.cn` 后用 3 分 24 秒拉完，镜像 1.66GB。",
          "",
          "**断点 2：宿主端口。** `-p 3000:8080` 里的 3000 是宿主端口，全局唯一。被别的进程或容器占了，docker 直接拒绝启动：实测报 `failed to bind host port 0.0.0.0:3000/tcp: address already in use`。",
          "",
          "**断点 3：启动耗时。** 容器状态 Up 不等于页面可用。Open WebUI 首次启动要在容器内准备 embedding 模型（日志里可见 `all-MiniLM-L6-v2`），本机实测从 `docker run` 到首次 HTTP 200 用了 **154 秒**——这期间浏览器就是连接被拒/转圈。很多人在这两分钟里以为装坏了，反复删容器重来，反而永远起不来。",
          "",
          "**断点 4：数据卷。** `/app/backend/data` 里放着 `webui.db`（账号、会话）、`uploads/`（上传文档）、`vector_db/`（RAG 向量库）。不挂 `-v`，这些都在容器的可写层，`docker rm` 即丢。实测 `docker inspect`：不挂卷的容器 Mounts 为空数组；挂了卷的容器重建后 `webui.db` 仍在（647168 → 684032 字节）。",
          "",
          "确定性推论：**拉不动先换源；端口报错先看谁占着；\"起不来\"先看日志是不是还在启动（没报 Traceback 就是等）；删容器前先确认 `docker inspect` 能看到挂载。**",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "docker rm -f open-webui 2>/dev/null",
          "docker run -d --name open-webui --restart always \\",
          "  -p 3000:8080 \\",
          "  -v open-webui:/app/backend/data \\",
          "  ghcr.nju.edu.cn/open-webui/open-webui:main",
          "# 首次启动约 2-3 分钟（容器内在准备 embedding 模型），期间页面打不开是正常的",
          "```",
          "",
          "端口 3000 被占就把左边的 3000 换成别的（如 3001）。删容器前数据都在 `open-webui` 卷里。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 镜像拉不动 → 换国内 ghcr 镜像前缀 — **必做**（官方源直连慢是常态）",
          "",
          "```bash",
          "docker pull ghcr.nju.edu.cn/open-webui/open-webui:main",
          "```",
          "",
          "实测 3 分 24 秒完成（1.66GB）。拉不动时也可以先开代理拉一次缓存、之后用国内前缀启动。",
          "",
          "### 2. 端口被占 → 先定位占用者，再换端口 — **必做**（宿主端口全局唯一）",
          "",
          "```bash",
          "docker ps | grep 3000        # 是不是旧容器占着",
          "ss -tlnp | grep 3000         # 是不是宿主进程占着",
          "```",
          "",
          "旧容器：`docker rm -f <名字>`；宿主进程：停掉或直接用别的宿主端口（`-p 3001:8080`）。",
          "",
          "### 3. 容器 Up 但页面打不开 → 看日志区分\"启动中\"和\"崩了\" — **排查**（90% 是还没启动完）",
          "",
          "```bash",
          "docker ps --filter name=open-webui",
          "docker logs --tail 50 open-webui",
          "```",
          "",
          "日志在刷 `Installing external dependencies` / 模型加载 = 还在启动，等到首次出现 `GET / HTTP/1.1\" 200` 再用浏览器；日志有 `Traceback` 才是真崩了。想确认进度，用命令轮询：",
          "",
          "```bash",
          "curl -s -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:3000/",
          "```",
          "",
          "### 4. 数据不能丢 → 必须挂 volume — **必做**（删容器是常规操作）",
          "",
          "```bash",
          "docker inspect open-webui --format '{{json .Mounts}}'",
          "```",
          "",
          "输出里要有 `\"Destination\":\"/app/backend/data\"`。升级镜像的正确姿势：`docker rm -f open-webui` 后用同一条 `docker run` 重建——卷还在，数据还在。",
          "",
          "### 5. 想连本机 ollama → 按场景指到宿主 — **按场景**",
          "",
          "容器里的 `localhost` 是容器自己；连宿主机 ollama 要指 `host.docker.internal`，见 [Open WebUI 连不上 Ollama](open-webui-ollama-connect.html)。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `docker ps` 里状态是 `Up`；",
          "- `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/` 返回 `200`（首次要等 2-3 分钟）；",
          "- `docker inspect open-webui --format '{{json .Mounts}}'` 能看到挂到 `/app/backend/data` 的卷；",
          "- `docker exec open-webui ls /app/backend/data` 里有 `webui.db`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- **Docker Desktop（Windows/macOS）**：端口映射后浏览器开 `http://localhost:3000` 即可；镜像源同理可用；本条目未在该形态下复现（本机是 WSL2 + 引擎直装）。",
          "- **docker compose**：把 `ports`、`volumes`、`image` 三段照上面写；端口冲突看 compose 内左侧值是否重复。",
          ""
        ],
        "症状": [
          "",
          "```",
          "# 形态 1：镜像拉取超时",
          "Error response from daemon: ... context deadline exceeded (Client.Timeout exceeded while awaiting headers)",
          "",
          "# 形态 2：宿主端口被占",
          "docker: Error response from daemon: failed to set up container networking:",
          "failed to bind host port 0.0.0.0:3000/tcp: address already in use",
          "",
          "# 形态 3：容器 Up 但浏览器打不开（启动未完成，最常见）",
          "（浏览器）This site can't be reached / 连接被拒绝",
          "",
          "# 形态 4：删容器重建后账号、会话、知识库清空",
          "（页面）回到首次注册引导",
          "```",
          ""
        ],
        "原因": [
          "",
          "镜像源慢（ghcr.io 直连）、宿主端口被占用、首次启动需 2-3 分钟准备 embedding 模型、数据目录 `/app/backend/data` 没有挂到 volume——四个断点分别对应上面四种形态。",
          ""
        ],
        "解法": [
          "",
          "1. 换国内镜像前缀：`ghcr.nju.edu.cn/open-webui/open-webui:main`（实测 3 分 24 秒拉完）；",
          "2. 端口冲突先 `docker ps` / `ss -tlnp` 定位，旧容器删掉或换宿主端口；",
          "3. 页面打不开先 `docker logs` 看是不是还在启动，等首次 200（本机实测冷启动 154 秒）；",
          "4. 一律加 `-v open-webui:/app/backend/data`，重建容器不丢数据；",
          "5. 连不上 ollama 另见 [open-webui-ollama-connect](open-webui-ollama-connect.html)。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-09-24，WSL2 Ubuntu + Docker 29.1.3，独立容器/独立卷，未碰其它容器）：",
          "  - 直连 `ghcr.io` 限时 25 秒未完成（部分层 Download complete，超时退出码 124）；`ghcr.nju.edu.cn` 实测 3 分 24.222 秒拉完，digest `sha256:8b432fe0a65b91116afc7961365c6cca5379cc923171386a96691f3471f3cae9`，大小 1656076851 字节；",
          "  - python3 占住宿主 3000 端口后 `docker run -p 3000:8080` → `failed to bind host port 0.0.0.0:3000/tcp: address already in use`（创建失败）；换 `-p 3300:8080` 成功；",
          "  - 冷启动到首次 HTTP 200 实测 154 秒；容器日志可见 embedding 模型加载（`all-MiniLM-L6-v2`）与 `uvicorn` 启动行；",
          "  - `-v` 挂卷：`docker inspect` Mounts 显示卷挂到 `/app/backend/data`；`docker rm -f` 后同卷重建，`webui.db` 仍存在（647168 → 684032 字节）；不挂卷容器 Mounts 为 `[]`；",
          "  - 证据：实测复现日志（2026-09-24）。",
          "- 工具侧实测素材（2026-09-01，实测复现日志（2026-09-01））：ghcr.nju.edu.cn 镜像全链路通过（连 ollama 真实推理）。",
          "- 未实测：Docker Desktop（Windows/macOS）形态、compose 形态（机制由官方文档与容器网络规则标注）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（daemon 没起，谈不上下载/启动）",
          "- **同根 / 相邻**：",
          "  - [docker pull 失败 / 镜像加速](docker-hub-pull-fail.html)（镜像源类问题的通用排查）",
          "  - [端口被占用 port is already allocated](docker-port-already-allocated.html)（端口断点的通用形态）",
          "- **同域（Open WebUI）**：",
          "  - [连不上 Ollama](open-webui-ollama-connect.html)",
          "  - [RAG 知识库检索不生效](open-webui-rag-embedding.html)",
          "  - [忘记管理员密码](open-webui-forgot-password.html)"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/open-webui-deploy-fail",
        "agent": "https://trove.xin/agent/problems/open-webui-deploy-fail"
      }
    },
    {
      "id": "open-webui-forgot-password",
      "title": "Open WebUI 忘记管理员密码 / 登录不进去怎么办",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "docker",
        "open-webui"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "本地容器实测（ghcr.nju.edu.cn/open-webui/open-webui:main，2026-08-25 构建）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "Open WebUI 装好后设过管理员密码，隔几天再登录想不起来。点\"忘记密码\"发现根本没有这个入口；想\"重新注册一个账号\"发现注册被拒（403）；网上教程说改 `user.password_hash`，但你连不上数据库，或者表里根本没有这一列。最后只能把容器删了重建——数据（聊天记录、知识库、用户）全没。其实不用删：密码就是 SQLite 里一个 bcrypt 哈希，直改它就能恢复登录。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "1. **密码只存在本地数据库**：Open WebUI 用 SQLite 存用户与密码（bcrypt 哈希，`$2b$12$...`），不做邮箱找回，所以没有\"忘记密码\"流程。",
          "2. **新版密码表换了**：`main`（2026-08 起的新版本）把密码从 `user` 表拆到独立 `auth` 表（`id / email / password / active`），`user` 表里**没有** `password_hash` 列。网上大量教程还停留在旧版 `user.password_hash`，照着改会报 `no such column`。",
          "3. **默认禁止注册**：生产默认 `ENABLE_SIGNUP=false`，注册接口直接 403——\"忘密码后重新注册一个管理员接管\"这条路默认走不通。",
          "4. **登录端点变了**：新版登录接口是 `POST /api/v1/auths/signin`（部分旧教程写 `/login`，新版返回 405）。",
          "",
          "密码重置难的本质是「凭据只在本地、校验不可逆」：Open WebUI 用 SQLite 存 bcrypt 哈希（`$2b$12$...`），登录时只做哈希比对，没有邮件服务也没有「重置」接口——「忘记密码」只能靠改库。",
          "",
          "坑有三层：①**表结构随版本演进**——新版（2026-08 起）把密码从 `user` 表拆到独立 `auth` 表（id/email/password/active），旧教程的 `user.password_hash` 在新版直接 `no such column`；",
          "",
          "②**bcrypt 哈希不能手写**——新密码必须用 bcrypt 生成（`htpasswd`/容器内 Python），直接 `UPDATE ... SET password='明文'` 会因校验失败永远登不进；",
          "",
          "③**默认禁注册**——`ENABLE_SIGNUP=false` 时注册接口 403，「重注册管理员接管」默认走不通。确定性推论：先确认版本（`docker ps` 镜像 tag）决定改哪张表；新密码必须过 bcrypt 再入库；改库前备份 webui.db，改完重启生效。",
          ""
        ],
        "快速修复": [
          "",
          "假设容器名是 `open-webui`、数据在容器内 `/app/backend/data/webui.db`（Docker 部署默认数据卷就是挂在这里）。一条命令重置密码：",
          "",
          "```bash",
          "# 替换成你自己的容器名 / 邮箱 / 新密码",
          "docker exec -it open-webui python3 <<'PY'",
          "import bcrypt, sqlite3",
          "EMAIL = \"admin@example.com\"        # 管理员登录邮箱",
          "NEW = \"你的新密码至少8位\"           # 新密码",
          "h = bcrypt.hashpw(NEW.encode, bcrypt.gensalt(rounds=12)).decode",
          "con = sqlite3.connect(\"/app/backend/data/webui.db\")",
          "n = con.execute(\"UPDATE auth SET password=? WHERE email=?\", (h, EMAIL)).rowcount",
          "con.commit",
          "print(\"updated rows:\", n)          # 必须输出 1；0 说明邮箱不对",
          "PY",
          "```",
          "",
          "改完**不需要重启容器**，直接去页面用新密码登录。`updated rows: 0` 就说明邮箱写错了，先查 `SELECT email FROM user`。",
          ""
        ],
        "官方做法（无 python 依赖，Docker 卷版）": [
          "",
          "官方文档（docs.openwebui.com/troubleshooting/password-reset/）用 `htpasswd` + `socat` 实现，不依赖容器内 python/bcrypt，适合不想进容器跑脚本的场景。改的仍是 `auth` 表（`id / email / password / active`），与本页主流程同一列。",
          "",
          "**第一步：生成本地 bcrypt 哈希**（宿主机执行；`htpasswd` 来自 apache2-utils，macOS/Linux 一般自带）：",
          "",
          "```bash",
          "# your-new-password 换成你的新密码；tr -d ':\\n' 去掉换行，输出一行哈希",
          "htpasswd -bnBC 10 \"\" \"your-new-password\" | tr -d ':\\n'",
          "```",
          "",
          "注意：哈希里的 `$` 字符放进下一条命令时要**三转义**（写成 `\\\\\\$`），否则会被 shell 展开。",
          "",
          "**第二步：一条命令写入**（`open-webui` 换成你的数据卷名，`HASH` 换成上一步输出，`admin@example.com` 换成管理员邮箱）：",
          "",
          "```bash",
          "docker run --rm -v open-webui:/data alpine/socat EXEC:\"bash -c 'apk add sqlite && echo UPDATE auth SET password='\\''HASH'\\'' WHERE email='\\''admin@example.com'\\''; | sqlite3 /data/webui.db'\", STDIO",
          "```",
          "",
          "**第二步（备选，一条命令失败时）**：部分环境 `alpine/socat` 镜像没有 bash，官方给了交替法——起一个 alpine 容器装工具再分步改：",
          "",
          "```bash",
          "docker run -it --rm -v open-webui:/data alpine",
          "# 容器内依次执行：",
          "apk add apache2-utils sqlite",
          "htpasswd -bnBC 10 \"\" \"your-new-password\" | tr -d ':'",
          "sqlite3 /data/webui.db",
          "UPDATE auth SET password='HASH' WHERE email='admin@example.com';",
          "# 退出 sqlite：Ctrl+D",
          "```",
          "",
          "**本地（pip）安装**：直接对数据文件跑 sqlite3，不用容器：",
          "",
          "```bash",
          "cd <open-webui 安装目录>",
          "sqlite3 backend/data/webui.db \"UPDATE auth SET password='HASH' WHERE email='admin@example.com';\"",
          "```",
          "",
          "官方还明确了一条兜底：**想完全重置（用户、设置、聊天、密码全清）就直接删 `webui.db`**——这是唯一的\"完全重置\"路径，删前确认你真的不要数据。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认你的部署方式，找到 webui.db — **必做**（改错文件全白干）",
          "",
          "- **Docker**：数据在数据卷里，容器内路径固定 `/app/backend/data/webui.db`。",
          "- **pip 直装**：数据在 `~/.open-webui/data/webui.db`（主进程运行中，`sqlite3` 或 `python3` 直接连）。",
          "- 无论哪种，改库前先备份：`cp webui.db webui.db.bak`。",
          "",
          "### 2. 查表结构（旧版新版不一样，先确认再改） — **必做**（新旧版表名不同，先确认再改）",
          "",
          "```bash",
          "docker exec open-webui python3 -c \"import sqlite3; con=sqlite3.connect('/app/backend/data/webui.db'); [print(r) for r in con.execute('PRAGMA table_info(auth)')]\"",
          "```",
          "",
          "- 有 `auth` 表且含 `password` 列 → 新版，改 `auth.password`（本页主流程）。",
          "- `auth` 表不存在但 `user` 表有 `password_hash` → 旧版（0.5.x 及更早），把上面命令里的 `UPDATE auth SET password=? WHERE email=?` 换成 `UPDATE user SET password_hash=? WHERE email=?`。",
          "",
          "### 3. 查管理员邮箱，别写错 — **必做**（邮箱写错会 `updated rows: 0`）",
          "",
          "```bash",
          "docker exec open-webui python3 -c \"import sqlite3; con=sqlite3.connect('/app/backend/data/webui.db'); [print(r) for r in con.execute('SELECT id,email,role FROM user')]\"",
          "```",
          "",
          "记下 `role=admin` 那行的 email。",
          "",
          "### 4. 生成 bcrypt 哈希并写入 — **必做**",
          "",
          "```bash",
          "docker exec -it open-webui python3 <<'PY'",
          "import bcrypt, sqlite3",
          "EMAIL = \"admin@example.com\"",
          "NEW = \"你的新密码至少8位\"",
          "h = bcrypt.hashpw(NEW.encode, bcrypt.gensalt(rounds=12)).decode",
          "con = sqlite3.connect(\"/app/backend/data/webui.db\")",
          "print(\"updated:\", con.execute(\"UPDATE auth SET password=? WHERE email=?\", (h, EMAIL)).rowcount)",
          "con.commit",
          "PY",
          "```",
          "",
          "### 5. 用新密码登录 — **必做**",
          "",
          "浏览器打开你的 Open WebUI 地址，用上面那个邮箱 + 新密码登录。如果开了多设备会话，旧会话会继续有效（不影响本次恢复登录）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 页面用新密码登录成功，且进去后仍是管理员（能进管理面板）。",
          "- 用**错误**的密码登录，仍然被拒（提示 email or password incorrect）——说明不是密码验证被绕过，而是哈希被正确替换。",
          "- 用 API 验证：`curl -X POST http://localhost:3000/api/v1/auths/signin -H 'Content-Type: application/json' -d '{\"email\":\"...\",\"password\":\"新密码\"}'` 返回 200 和 `token`。",
          ""
        ],
        "WEBUI_SECRET_KEY：为什么有时改完密码还是被登出": [
          "",
          "改密码本身不需要动 `WEBUI_SECRET_KEY`，旧会话继续有效（本页实测）。但以下情况会\"强制全员下线\"，与密码无关：",
          "",
          "- **没设 `WEBUI_SECRET_KEY`**：Open WebUI 每次启动生成随机 key，重启后旧会话 JWT 全部失效 → 全员重新登录（官方 FAQ 确认）。",
          "- **换了 key**：除强制下线外，OAuth token / 工具 API key 用该 key 加密，换 key 后解密失败，报 `Error decrypting tokens`（官方 FAQ 确认）。",
          "- **多实例 / 复用同一数据卷**：必须所有实例用同一个固定 key，否则实例间会话与 OAuth 状态互相失效。",
          "",
          "所以恢复登录后建议把 `WEBUI_SECRET_KEY` 固定成一个长随机串（Docker 加 `-e WEBUI_SECRET_KEY=...`；Compose 写进 environment），一劳永逸。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- **旧版 Open WebUI（0.5.x 及更早）**：密码列在 `user.password_hash`，其余流程相同。",
          "- **Docker 卷在宿主机上**：也可以挂载着直接在宿主机改（`sqlite3 <卷路径>/webui.db` + 任意 bcrypt 工具），但注意容器写缓存，改完再启动容器最稳。",
          "- **接了 LDAP / SSO / 企业登录**：密码由身份提供方管，本地改 `auth` 表只对本地账号有效，域账号去 IdP 那边重置。",
          "- **忘记的是 WebUI 而非 Ollama 的密码**：Ollama 本身默认无鉴权，连不上是 `OLLAMA_BASE_URL` / 容器内 localhost 问题，见 open-webui-ollama-connect。",
          ""
        ],
        "症状": [
          "",
          "- 登录页提示 `The email or password provided is incorrect`，且想不起来密码。",
          "- 注册接口报 403 `You do not have permission to access this resource`。",
          "- 按旧教程找 `user.password_hash`，sqlite 报 `no such column: password_hash`。",
          ""
        ],
        "原因": [
          "",
          "- 密码为本地 SQLite 中 bcrypt 哈希，无找回流程；新版拆到 `auth` 表（无 `password_hash` 列）；默认禁止注册（`ENABLE_SIGNUP=false`），无法用新账号接管。",
          ""
        ],
        "解法": [
          "",
          "- 备份 `webui.db` → 容器内 python3（自带 bcrypt/sqlite3）生成新哈希 → `UPDATE auth SET password=? WHERE email=?`（旧版改 `user.password_hash`）→ 直接新密码登录，无需重启。",
          ""
        ],
        "验证证据": [
          "",
          "- 实测容器 `owui-pw`（`ghcr.nju.edu.cn/open-webui/open-webui:main`，2026-08-25 构建，端口 3002）：",
          "  - 错误密码登录 → HTTP 400 `The email or password provided is incorrect`；",
          "  - 容器内 `bcrypt.hashpw(..., gensalt(rounds=12))` 生成 `$2b$12$` 哈希（bcrypt 5.0.0）；",
          "  - `UPDATE auth SET password=? WHERE email=?` → `rows updated: 1`（app 运行中直改，无需停服）；",
          "  - 改后错误密码 → 仍 400；新密码 → HTTP 200 返回 `role: admin` + JWT；",
          "  - 新 token 调 `GET /api/v1/auths/` → HTTP 200（会话真实有效）；",
          "  - `bcrypt.checkpw(新密码, 库中哈希) = True`，`checkpw(错误密码) = False`；",
          "  - 测试容器与数据卷已清理（`docker rm -f` + `docker volume rm`）。",
          "- 完整日志：`实测复现日志（2026-08-30）`",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **同根（同一根因）**：",
          "  - [RAG 知识库检索不生效 / embedding 配置错](open-webui-rag-embedding.html)（同根：webui.db 落库优先——auth 表改密码与 config 表改配置都覆盖 env/注册）",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Open WebUI 页面显示连不上 Ollama](open-webui-ollama-connect.html)（相邻：WebUI 层账号登录 vs 模型通道）",
          ""
        ],
        "参考资料": [
          "",
          "- Open WebUI 官方文档：Reset Admin Password — <https://docs.openwebui.com/troubleshooting/password-reset/>",
          "- Open WebUI 官方 FAQ（WEBUI_SECRET_KEY 与登出 / 解密错误）— <https://docs.openwebui.com/faq/>"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/open-webui-forgot-password",
        "agent": "https://trove.xin/agent/problems/open-webui-forgot-password"
      }
    },
    {
      "id": "open-webui-ollama-connect",
      "title": "Docker 里跑 Open WebUI 连不上 Ollama（页面显示 Ollama 未连接 / 模型列表为空）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "open-webui",
        "docker"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "实测复现日志（2026-08-30）（工具侧实测证据，2026-08-30）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "用 Docker 部署 Open WebUI 后，页面顶部一直提示 Ollama 未连接，模型下拉是空的；`docker exec` 进容器访问 `localhost:11434` 报 `Connection refused`。最常见的起因：照着\"本机安装教程\"把 `OLLAMA_BASE_URL` 配成了 `http://localhost:11434`——**在容器里这是错的**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Open WebUI 跑在 Docker 容器里，**容器内的 `localhost` 是容器自己，不是宿主机**。Ollama 默认监听宿主机 `127.0.0.1:11434`，容器里 `localhost:11434` 上没有任何服务 → 拒绝连接（实测：`Errno 111 Connection refused`）。要访问宿主机的 Ollama，必须用**容器网络视角下宿主机的地址**：",
          "",
          "| 环境 | 宿主机地址 | 说明 |",
          "|---|---|---|",
          "| Docker Desktop（Windows/macOS） | `host.docker.internal` | 默认可用 |",
          "| Linux Docker 引擎 | `host.docker.internal` | 需 `--add-host host.docker.internal:host-gateway` 映射（本机 Docker 29.1.3 实测默认已映射，显式加无害） |",
          "| docker compose | `host.docker.internal` | 用 `extra_hosts: [\"host.docker.internal:host-gateway\"]` |",
          "",
          "注意新版 Open WebUI 用复数环境变量 `OLLAMA_BASE_URLS`（旧版是单数 `OLLAMA_BASE_URL`），配错变量名同样连不上。",
          "",
          "Open WebUI 在容器里，Ollama 在宿主机——「容器内 localhost 是容器自己」这条网络命名空间规则（见 docker-host-access）在这里直接表现为 `Connection refused`。",
          "",
          "配置层还有一个版本演进坑：新版 Open WebUI 的配置键是复数 `OLLAMA_BASE_URLS`（旧版单数 `OLLAMA_BASE_URL`）——变量名错了，配置静默不生效，界面照样报连不上。",
          "",
          "确定性推论：连不上先分清是「寻址层」还是「配置键层」——容器内 `curl http://host.docker.internal:11434` 通 = 寻址没问题，问题在 Open WebUI 的配置键/值（URL 是否带 http://、键名是否复数）；",
          "",
          "Docker Desktop 默认注入 host.docker.internal，Linux 引擎需 `--add-host host.docker.internal:host-gateway`；改完环境变量要重启容器（env 只在启动时读）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：重建容器，指到宿主机 Ollama + Linux 显式加 host-gateway 映射",
          "docker rm -f open-webui",
          "docker run -d --name open-webui --restart always -p 3000:8080 \\",
          "  --add-host host.docker.internal:host-gateway \\",
          "  -e OLLAMA_BASE_URLS=http://host.docker.internal:11434 \\",
          "  -v open-webui:/app/backend/data \\",
          "  ghcr.nju.edu.cn/open-webui/open-webui:main",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认是\"容器内地址\"问题（30 秒定位） — **必做**（先确认是不是这个坑，再动手）",
          "",
          "```bash",
          "docker exec open-webui python3 -c \"import urllib.request",
          "for u in ['http://localhost:11434/api/version', 'http://host.docker.internal:11434/api/version']:",
          "    try: print(u, '-> OK:', urllib.request.urlopen(u, timeout=5).read.decode.strip)",
          "    except Exception as e: print(u, '-> FAIL:', str(e)[:60])\"",
          "```",
          "",
          "实测输出形态：`localhost:11434 -> FAIL: Connection refused`，`host.docker.internal:11434 -> OK: {\"version\":\"0.32.14\"}`。命中即本坑。",
          "",
          "### 2. 检查容器启动参数 — **排查**（看是否漏了 --add-host / 变量名）",
          "",
          "`docker inspect open-webui` 看 `Config.Env` 里 `OLLAMA_BASE_URLS` 是否指向 `localhost`，`HostConfig.ExtraHosts` 是否有 host-gateway 映射（Linux 引擎）。",
          "",
          "### 3. 按上表修正并重建容器 — **必做**（改完重建才生效）",
          "",
          "- 值：`http://host.docker.internal:11434`（不要带尾斜杠）；",
          "- 变量名：新版用 `OLLAMA_BASE_URLS`；",
          "- Linux：加 `--add-host host.docker.internal:host-gateway`；compose 用 `extra_hosts`。",
          "",
          "### 4. 验证 — **必做**（容器内外各验一次）",
          "",
          "- 容器内视角：`host.docker.internal:11434` 返回 Ollama 版本号（实测通过）；",
          "- 页面：刷新后模型下拉出现宿主机 Ollama 的模型列表（工具侧联调实测：open-webui 界面发现 ollama 模型通过）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`docker exec open-webui python3 -c \"import urllib.request; print(urllib.request.urlopen('http://host.docker.internal:11434/api/tags', timeout=5).read[:200])\"` 返回模型列表；",
          "",
          "页面模型下拉能看到宿主机的模型（如 qwen2.5-0.5b、nomic-embed-text）。",
          ""
        ],
        "反向场景：外部设备 / 其他容器连不上 Ollama": [
          "",
          "镜像坑：页面里地址写对了，但 Ollama 服务本身只监听 `127.0.0.1`——局域网里的手机/另一台机器、别的容器、远程 IDE 全都连不上（报 `connection refused`）。Ollama 默认只绑本机回环，要对外提供服务必须改监听地址：",
          "",
          "```bash",
          "# 服务侧：监听所有网卡（局域网 / 容器网络可访问）",
          "OLLAMA_HOST=0.0.0.0:11434 ollama serve",
          "# systemd 安装：/etc/systemd/system/ollama.service 的 [Service] 加",
          "#   Environment=\"OLLAMA_HOST=0.0.0.0:11434\"",
          "# Docker 安装：启动加 -e OLLAMA_HOST=0.0.0.0:11434",
          "```",
          "",
          "改完从外部验证：`curl http://<宿主机IP>:11434/api/version` 返回 `{\"version\":\"...\"}` 即对外可达。",
          "",
          "注意两点：**监听 0.0.0.0 = 对局域网开放 API**（ollama 默认无鉴权，任何人能拉模型列表/调推理，生产环境别裸奔）；旧版本曾有回归只监听 localhost（[ollama/ollama#12053](https://github.com/ollama/ollama/issues/12053)，v0.11.5/0.11.6 已修复），升版也是一种解法。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 变量名 | 新版 `OLLAMA_BASE_URLS`（复数）；旧教程写 `OLLAMA_BASE_URL`（单数）可能不生效 |",
          "| Linux 引擎 | `host.docker.internal` 需要 `--add-host host.docker.internal:host-gateway`；本机 Docker 29.1.3 实测默认已映射，显式加无害 |",
          "| docker compose | `environment` + `extra_hosts: [\"host.docker.internal:host-gateway\"]` |",
          "| UI 表现 | Open WebUI 对 Ollama 是懒连接，匿名请求/日志不一定能看到报错；以容器内实测为准 |",
          "| 未实测 | 非 Linux 平台（Docker Desktop win/mac 的 host.docker.internal 默认行为）未在本机复现，按官方机制标注 |",
          ""
        ],
        "症状": [
          "",
          "Docker 部署的 Open WebUI 页面提示 Ollama 未连接、模型列表为空；容器内访问 `localhost:11434` 拒绝连接。",
          ""
        ],
        "原因": [
          "",
          "容器内 `localhost` 是容器自己，不是宿主机；`OLLAMA_BASE_URLS` 指向 `localhost:11434` 时连不到宿主机的 Ollama。正确做法是用 `host.docker.internal`（Linux 引擎需 host-gateway 映射）。",
          ""
        ],
        "解法": [
          "",
          "1. `docker exec` 容器内实测两个地址，确认 localhost 拒绝、host.docker.internal 可达；",
          "2. 重建容器：`-e OLLAMA_BASE_URLS=http://host.docker.internal:11434`（新版用复数变量名）；",
          "3. Linux 引擎加 `--add-host host.docker.internal:host-gateway`（compose 用 `extra_hosts`）；",
          "4. 刷新页面确认模型列表出现。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-08-30，WSL2 Ubuntu + Docker 29.1.3 + ollama 0.32.14）：独立容器（端口 3001 + 独立 volume，未碰工具侧容器）内 `localhost:11434` → `Errno 111 Connection refused`；`host.docker.internal:11434` → `{\"version\":\"0.32.14\"}`。`/api/models` 匿名请求 401（懒连接 + 需登录会话），UI 未连接横幅未截获，机制层面已坐实。证据：实测复现日志（2026-08-30）。",
          "- 工具侧联调证据（2026-08-30，实测复现日志（2026-08-30））：`--add-host=host.docker.internal:host-gateway` + `OLLAMA_BASE_URLS=http://host.docker.internal:11434` 容器健康，`docker exec` 访问 `/api/tags` 通过，open-webui 界面发现 ollama 模型。",
          "- 未实测：Docker Desktop（Windows/macOS）的 host.docker.internal 默认行为（机制由官方文档标注）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [RAG 知识库检索不生效 / embedding 配置错](open-webui-rag-embedding.html)（RAG_OLLAMA_BASE_URL 是独立键：聊天层连上 ≠ 检索层连上）",
          "  - [忘记管理员密码 / 登录不进去](open-webui-forgot-password.html)（WebUI 账号登录层问题）",
          "  - [ollama 报 could not connect](ollama-connect-failed.html)（先确认 ollama 服务本身在跑）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/open-webui-ollama-connect",
        "agent": "https://trove.xin/agent/problems/open-webui-ollama-connect"
      }
    },
    {
      "id": "open-webui-rag-embedding",
      "title": "Open WebUI 知识库检索不生效 / embedding 配置错（上传文件报 Ollama embed 404）",
      "domain": "local-llm",
      "domain_label": "大模型本地部署",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "open-webui",
        "ollama"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "本地容器实测（ghcr.nju.edu.cn/open-webui/open-webui:main + ollama/ollama:latest，2026-08-30）",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "给 Open WebUI 建了知识库、上传了文档，文件状态一直是 `failed`，报错：",
          "",
          "```text",
          "Ollama embed error (404): model \"nomic-embed-text\" not found, try pulling it first",
          "```",
          "",
          "模型明明拉过了、Ollama 也连得上（聊天能看到模型），但 embedding 就是不走。网上教程让你设 `OLLAMA_BASE_URLS`、`RAG_EMBEDDING_MODEL`，全设了还是不行。这里有三层坑，缺一层就 404。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "embedding 链路有三层独立配置，任何一层错都会 404：",
          "",
          "| 坑 | 配置项 | 说明 |",
          "|---|---|---|",
          "| 1. 模型名带 `ollama/` 前缀 | `RAG_EMBEDDING_MODEL` | 引擎选 ollama 时必须写裸名 `nomic-embed-text`；写 `ollama/nomic-embed-text` 会被原样发给 ollama → `model \"ollama/nomic-embed-text\" not found` |",
          "| 2. embedding 的 URL 是独立键 | `RAG_OLLAMA_BASE_URL`（DB: `rag.ollama.base_url`） | **不是** `OLLAMA_BASE_URLS`（后者只控制模型列表/聊天）。缺省回退到镜像默认（本镜像实测 `http://host.docker.internal:11434`），那个实例可能没有 embedding 模型 → 404 |",
          "| 3. 首次启动即落库，env 不再生效 | DB `config` 表 | 配置在第一次运行时写入 SQLite，之后改环境变量重启**无效**（DB 优先于 env）——改配置要走管理 API |",
          "",
          "另外注意：默认 `RELEVANCE_THRESHOLD=0`，无关查询也会返回 top-k 结果，这不是\"检索坏了\"。",
          "",
          "> 两种场景二选一：**已有实例**（99% 的情况）用管理 API 一行改；**全新容器**用启动参数一次给全。",
          "",
          "embedding 链路是「模型名 → embedding 服务地址 → 存储配置」三段串联，任何一段错都表现为「上传文件 failed / 检索 404」。",
          "",
          "三段各自的机制：①模型名带 `ollama/` 前缀会被**原样转发**给 ollama（引擎名不是命名空间），ollama 侧查不到 `ollama/nomic-embed-text` 就 404；",
          "",
          "②embedding 服务地址是**独立配置键** `RAG_OLLAMA_BASE_URL`（DB `rag.ollama.base_url`），与聊天的 `OLLAMA_BASE_URLS` 无关——缺省回退镜像默认值（可能指向没有 embedding 模型的实例）；",
          "",
          "③配置**首次启动即落库**（SQLite config 表），之后 env 不再生效，DB 优先于 env——这是「改环境变量重启无效」的根因。",
          "",
          "确定性推论：先分清场景——已有实例用管理 API（/api/v1/retrieval/embedding/update）改 DB 配置，全新容器用启动参数一次给全；改完上传文件验证向量化成功（2s 级）而非只看配置页；`RELEVANCE_THRESHOLD=0` 时无关查询也返回 top-k 是正常行为，不是检索坏了。",
          ""
        ],
        "快速修复": [
          "",
          "已有数据卷/改过配置的实例，用管理 API 一行改（也等于图形界面\"检索设置 → embedding\"里的操作）：",
          "",
          "```bash",
          "# 把 url 换成你的 ollama 地址；模型名写裸名，别带 ollama/ 前缀",
          "curl -X POST http://localhost:3000/api/v1/retrieval/embedding/update \\",
          "  -H \"Authorization: Bearer <你的JWT>\" -H 'Content-Type: application/json' \\",
          "  -d '{\"RAG_EMBEDDING_ENGINE\":\"ollama\",\"RAG_EMBEDDING_MODEL\":\"nomic-embed-text\",\"ollama_config\":{\"url\":\"http://host.docker.internal:11434\",\"key\":\"\"}}'",
          "```",
          "",
          "然后**删除之前 failed 的文件重新上传**（旧文件不会自动重试）。",
          "",
          "全新容器：启动参数一次给全（五件套）：",
          "",
          "```bash",
          "docker run -d -p 3000:8080 --add-host host.docker.internal:host-gateway \\",
          "  -v open-webui:/app/backend/data --name open-webui \\",
          "  -e ENABLE_SIGNUP=true \\",
          "  -e RAG_EMBEDDING_ENGINE=ollama \\",
          "  -e RAG_EMBEDDING_MODEL=nomic-embed-text \\",
          "  -e RAG_OLLAMA_BASE_URL=http://host.docker.internal:11434 \\",
          "  -e OLLAMA_BASE_URLS=http://host.docker.internal:11434 \\",
          "  ghcr.nju.edu.cn/open-webui/open-webui:main",
          "```",
          ""
        ],
        "官方排障对照（docs.openwebui.com/troubleshooting/rag/）": [
          "",
          "官方 RAG 排障文档与本页三坑链直接相关的内容，并排看更清楚：",
          "",
          "| 官方条目 | 与本页的关系 |",
          "|---|---|",
          "| `400: 'NoneType' object has no attribute 'encode'` | **姊妹报错**：嵌入模型没配好 / 没下载完 / 外部嵌入服务不可达时，Open WebUI 生成向量失败。与\"文件 failed + `Ollama embed error (404)`\"同根因、不同形态；官方解法：管理面板 → 文档 → Embedding Model **重新保存一次**（强制重查/重下） |",
          "| API 上传后立刻加库 → `400: The content provided is empty` | **竞态条件**：上传接口立即返回 file_id，内容提取与向量化在后台异步进行；还没处理完就加库，系统看到的是空内容。官方解法：轮询 `GET /api/v1/files/{file_id}/process/status` 直到 `completed` 再 `knowledge/<kb_id>/file/add` |",
          "| `ENABLE_KB_EXEC=True` | 模型有原生函数调用时，给模型一个文件系统式知识库接口（ls/tree/grep/cat/read-by-line），比单个检索工具更可靠；Legacy Mode 下无效 |",
          "| 换嵌入模型后必须重索引 | 官方明确：换模型后到 管理面板 → 文档 → Reindex 全部已有文档，否则新向量不生效（与本页坑 3\"落库优先\"同一个道理） |",
          "| 嵌入模型选型 | 单用户起步：all-MiniLM-L6-v2（默认，本地跑）；多用户且有 Ollama：nomic-embed-text via Ollama（向量化卸载给 Ollama）；生产有 API 预算：text-embedding-3-small；完全离线：nomic-embed-text 或 mxbai-embed-large via Ollama |",
          "| 2048 token 限制 | Ollama 默认模型上下文常只有 2048 token，检索再多也只吃进一小截。官方：Ollama 模型在 管理面板 → 模型 → 高级参数 调大 context length（网页检索建议 8192+，复杂网页 16384+）；也可开 Full Context Mode / Bypass Embedding |",
          "| `RAG_SYSTEM_CONTEXT=True` | 把 RAG 上下文注入 system message（固定位置）而非用户消息，配合 KV prefix cache 让追问几乎瞬时；追问答越来越慢时可开 |",
          "",
          "**API 上传的正确姿势**（官方 wait_for_processing 思路，避免空内容竞态）：",
          "",
          "```python",
          "import requests, time",
          "",
          "def wait_for_processing(token, file_id, timeout=300):",
          "    url = f\"http://localhost:3000/api/v1/files/{file_id}/process/status\"",
          "    headers = {\"Authorization\": f\"Bearer {token}\"}",
          "    start = time.time",
          "    while time.time - start < timeout:",
          "        r = requests.get(url, headers=headers).json",
          "        if r.get(\"status\") == \"completed\":",
          "            return True",
          "        if r.get(\"status\") == \"failed\":",
          "            raise RuntimeError(f\"Processing failed: {r.get('error')}\")",
          "        time.sleep(2)          # 每 2s 轮询一次",
          "    raise TimeoutError(\"file processing timed out\")",
          "```",
          "",
          "本页实测与官方行为一致的一条补充：`RELEVANCE_THRESHOLD` 默认 0 时无关查询也返回 top-k 结果——这不是检索坏了，要过滤就调高阈值。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先直连 ollama 验证模型与命名 — **排查**（30 秒定位：先分清模型问题还是配置问题）",
          "",
          "```bash",
          "# 裸名（正确）：应 200，返回 embeddings",
          "curl -s http://localhost:11434/api/embed -d '{\"model\":\"nomic-embed-text\",\"input\":[\"测试\"]}' | head -c 200",
          "# ollama/ 前缀（错误示范）：应 404 model \"ollama/nomic-embed-text\" not found",
          "curl -s http://localhost:11434/api/embed -d '{\"model\":\"ollama/nomic-embed-text\",\"input\":[\"测试\"]}'",
          "```",
          "",
          "模型不在：`ollama pull nomic-embed-text`（274MB，官方 registry 小模型通常可达）。",
          "",
          "### 2. 看文件失败的具体报错 — **排查**（按报错文本分层）",
          "",
          "文件列表里点开失败文件，错误信息里如果是 `Ollama embed error (404)`，就是上面的三层坑；如果是 `Connection refused`，才是连不上 ollama（见 open-webui-ollama-connect）。",
          "",
          "### 3. 已有数据卷：用管理 API 改，别改 env — **必做**（改配置；env 落库后无效）",
          "",
          "```bash",
          "# 确认当前生效值（DB 优先）",
          "docker exec open-webui python3 -c \"import sqlite3; con=sqlite3.connect('/app/backend/data/webui.db'); print(con.execute(\\\"SELECT key,value FROM config WHERE key='rag.ollama.base_url'\\\").fetchone)\"",
          "# 用管理 API 覆盖（图形界面等价：管理面板 → 设置 → 检索 → embedding）",
          "curl -X POST http://localhost:3000/api/v1/retrieval/embedding/update \\",
          "  -H \"Authorization: Bearer <token>\" -H 'Content-Type: application/json' \\",
          "  -d '{\"RAG_EMBEDDING_ENGINE\":\"ollama\",\"RAG_EMBEDDING_MODEL\":\"nomic-embed-text\",\"ollama_config\":{\"url\":\"http://host.docker.internal:11434\",\"key\":\"\"}}'",
          "```",
          "",
          "### 4. 删除失败文件，重新上传 — **必做**（旧文件不会自动重试）",
          "",
          "上传 → 等文件状态变 `completed`（几秒到几十秒）。还 failed 就再看错误文本，按层排查。",
          "",
          "### 5. 建库 → 加文件 → 检索验证 — **必做**（验收）",
          "",
          "```bash",
          "# 建知识库 → 得到 kb_id",
          "curl -X POST http://localhost:3000/api/v1/knowledge/create -H \"Authorization: Bearer <token>\" -H 'Content-Type: application/json' -d '{\"name\":\"kb\"}'",
          "# 加文件（file_id 来自上传响应）",
          "curl -X POST http://localhost:3000/api/v1/knowledge/<kb_id>/file/add -H \"Authorization: Bearer <token>\" -H 'Content-Type: application/json' -d '{\"file_id\":\"<file_id>\"}'",
          "# 检索",
          "curl -X POST http://localhost:3000/api/v1/retrieval/query/collection -H \"Authorization: Bearer <token>\" -H 'Content-Type: application/json' -d '{\"collection_names\":[\"<kb_id>\"],\"query\":\"文档里的关键词\",\"k\":3}'",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 上传文件状态 `completed`（不再 failed），日志出现 `embeddings generated ... added ... to collection`。",
          "- 检索命中文档内容，且元数据里 `embedding_config` 正确：`{'engine': 'ollama', 'model': 'nomic-embed-text'}`。",
          "- 直连 `POST /api/embed` 裸名返回 200（768 维向量）；前缀返回 404 属预期。",
          "- 注意：无关查询也返回结果 ≠ 修复失败（默认阈值 0，返回 top-k）；要过滤在管理面板调高 `RELEVANCE_THRESHOLD`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 旧版 Open WebUI | 嵌入配置在管理面板检索设置里，同样分\"引擎/模型名/URL\"三处；URL 别写成模型列表的 OLLAMA_BASE_URLS |",
          "| OpenAI 兼容 embedding | 引擎选 openai，配 `openai_config` 的 base_url + key，模型名写供应商的（如 text-embedding-3-small） |",
          "| Docker Desktop / Linux | `host.docker.internal` 需 `--add-host host.docker.internal:host-gateway`（Linux）；Docker Desktop 默认支持 |",
          "| 聊天层自动检索 | 依赖模型工具调用能力：0.5B 小模型实测不触发检索（工具侧证据），建议 ≥7B 支持工具调用的模型或提示词注入知识内容 |",
          "| 升级/复用旧卷 | 旧配置落库优先于新 env——务必走管理 API 或直改 DB |",
          "| 挂载数据目录（fresh 部署） | bind-mount 数据目录可能掩盖内置 embedding 模型（open-webui#29014 外部报告，未本地复现）；挂卷部署后确认 Embedding Model 列表里还有可用模型 || 升级/复用旧卷 | 旧配置落库优先于新 env——务必走管理 API 或直改 DB |",
          ""
        ],
        "症状": [
          "",
          "- 知识库文件一直 `failed`，错误含 `Ollama embed error (404): model \"nomic-embed-text\" not found, try pulling it first`；",
          "- 模型列表能看到 embedding 模型、聊天正常，但知识库检索不生效；",
          "- 按教程设了 `OLLAMA_BASE_URLS` / `RAG_EMBEDDING_MODEL` 后重启仍 404。",
          ""
        ],
        "原因": [
          "",
          "- 模型名被写成带 `ollama/` 前缀（404）；",
          "- embedding 的 URL 是独立配置 `RAG_OLLAMA_BASE_URL`，没设时回退到默认实例（可能没有该模型）；",
          "- 首次启动配置落 SQLite 后 DB 优先于 env，改 env 无效。",
          ""
        ],
        "解法": [
          "",
          "1. 直连 `POST /api/embed` 裸名 200 / 前缀 404 定位；2. `ollama pull` 补 embedding 模型；3. 已有卷用管理 API `POST /api/v1/retrieval/embedding/update` 写 `ollama_config.url`（env 无效）；4. 删 failed 文件重传，等 `completed`；5. 建库+加文件+检索命中验证；6. 无关查询返回 top-k 时调高 RELEVANCE_THRESHOLD。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-08-30，独立容器 3004/11435，未碰工具侧与系统 snap）：",
          "  - 直连 `/api/embed`：裸名 `nomic-embed-text` → 200（768 维，L2 norm=1.0）；`ollama/nomic-embed-text` → 404 `model \"ollama/nomic-embed-text\" not found`；",
          "  - 首次启动缺 `RAG_OLLAMA_BASE_URL` → 上传文件 failed，错误 `Ollama embed error (404)...`；DB `rag.ollama.base_url` 被持久化为默认 `host.docker.internal:11434`；",
          "  - 重启补 env `RAG_OLLAMA_BASE_URL=...11435` → **仍 404**（DB 优先于 env，坑 2 完整机制）；",
          "  - 管理 API `POST /api/v1/retrieval/embedding/update` 写 `ollama_config.url` → DB 变 11435；",
          "  - 重传文件 → 2s 内 `completed`；建库+加文件 → 200；检索\"默认管理端口是多少\" → hits=1，命中文档内容含\"默认管理端口:8123\"，meta 含 `embedding_config: {'engine': 'ollama', 'model': 'nomic-embed-text'}`；",
          "  - 无关查询（\"量子计算与星际旅行\"）也返回 1 条 → 默认阈值 0 返回 top-k；",
          "  - 测试容器/卷已清理。",
          "- 聊天层自动检索限制：工具侧实测（实测复现日志（2026-08-30））：0.5B 模型不触发工具调用、输出幻觉；检索层本身可用。",
          "- 完整日志：`实测复现日志（2026-08-30）`",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **同根（同一根因）**：",
          "  - [忘记管理员密码 / 登录不进去](open-webui-forgot-password.html)（同根：webui.db 是唯一事实源（config 表与 auth 表都优先于 env/注册））",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [Open WebUI 页面显示连不上 Ollama](open-webui-ollama-connect.html)（Connection refused 类，host.docker.internal 与 OLLAMA_BASE_URLS）",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（拉 embedding 模型慢）",
          "  - [模型目录换盘](ollama-models-migration.html)（embedding 模型也在模型目录里，一起搬）",
          ""
        ],
        "参考资料": [
          "",
          "- Open WebUI 官方 RAG 排障文档 — <https://docs.openwebui.com/troubleshooting/rag/>"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/open-webui-rag-embedding",
        "agent": "https://trove.xin/agent/problems/open-webui-rag-embedding"
      }
    },
    {
      "id": "pep668-externally-managed",
      "title": "Ubuntu/Debian pip install 报 error: externally-managed-environment（PEP 668）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "python",
        "pip"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/weixizhou/p/20271782",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "新装的 Ubuntu（或 WSL）上 `pip install` 任何包都直接报 `error: externally-managed-environment`，连 `pip install numpy` 都装不了。你以为是 Python 3.14 太新、或 pip 坏了，甚至想降级 Python——其实都不是：这是**发行版的默认安全策略**，pip 只是按规矩拒绝往系统环境里装。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Ubuntu 23.04 起默认开启 **PEP 668**：系统 Python 被标记为 \"externally managed\"，pip 拒绝直接向系统环境安装第三方包，防止第三方包覆盖 apt 管理的系统包、把系统 Python 搞坏。",
          "",
          "- 报错与 Python 版本无关，**任何包、任何时候**都触发，这是设计行为。",
          "- 正确解法不是绕过检查，而是**改用虚拟环境**：venv 是独立环境，pip 随便装，与系统互不污染。",
          "",
          "> 实测环境：WSL Ubuntu，Python 3.14.4 / pip 25.1.1，2026-08-29。",
          "",
          "PEP 668 是「环境所有权」声明：系统 Python 的 site-packages 由 apt/发行版包管理器持有，pip 直接写入会与 apt 的包记录脱节（apt 不知道 pip 装了什么、升级时可能覆盖，pip 也不知道 apt 动过什么）。发行版用一个标记文件（`EXTERNALLY-MANAGED`）声明所有权，pip 检测到就拒绝。",
          "",
          "确定性推论：报 `externally-managed-environment` 与包名/版本无关，是**目标环境**的问题；解法是换环境而不是绕过（`--break-system-packages` 是「我知道会弄脏系统」的显式声明，只适合容器/一次性环境）；venv 之所以永远不报，是因为它自带独立 site-packages 且不声明 externally-managed；",
          "",
          "判断路径固定：先 `python3 -m venv .venv` 建独立环境再 pip，而不是跟系统 Python 搏斗。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：建虚拟环境并激活，之后 pip install 不再报错",
          "python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 建虚拟环境 — **必做**",
          "",
          "```bash",
          "python3 -m venv .venv",
          "```",
          "",
          "报 `ensurepip is unavailable` 就先装：`sudo apt install python3-venv`（精简镜像常见）。",
          "",
          "### 2. 激活 — **必做**",
          "",
          "```bash",
          "source .venv/bin/activate",
          "```",
          "",
          "激活后提示符前出现 `(.venv)`，此时 `pip` 已指向虚拟环境（实测：venv 内 `pip install six` 直接成功）。",
          "",
          "### 3. 正常安装 — **必做**",
          "",
          "```bash",
          "pip install -r requirements.txt",
          "```",
          "",
          "以后进项目先 `source .venv/bin/activate` 即可，不再遇到 PEP 668。",
          "",
          "### 4. 命令行工具用 pipx（可选） — **进阶**",
          "",
          "系统级命令行应用优先 `pipx install <应用>`——pipx 自动为每个应用建独立虚拟环境，不碰系统 Python，也不报 PEP 668。",
          "",
          "> 单次救急可用 `pip install <包> --break-system-packages`，但会跳过保护、可能覆盖系统包，不推荐长期用。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "source .venv/bin/activate && pip install six && python -c \"import six; print(six.__version__)\"",
          "```",
          "",
          "不再报 `externally-managed-environment`、能正常 import 即成功（实测：venv 内安装成功；dry-run `--break-system-packages` 路径也验证过会输出 `Would install six-1.17.0`）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Ubuntu 23.04+ / Debian 12+ | 默认开启 PEP 668，必须 venv/pipx/--break-system-packages |",
          "| 旧版 Ubuntu（22.04 及以下） | 不强制，但同样推荐 venv |",
          "| WSL Ubuntu | 与原生 Ubuntu 行为一致 |",
          "| 精简镜像 | 常缺 `python3-venv`，先 `apt install python3-venv` |",
          ""
        ],
        "症状": [
          "",
          "在 Ubuntu/Debian（含 WSL 新装的 Ubuntu 26.04）上直接 `pip install <包>` 立即报错：",
          "",
          "```",
          "error: externally-managed-environment",
          "× This environment is externally managed",
          "╰─> To install Python packages system-wide, try apt install ...",
          "```",
          "",
          "特征：装**任何**包都失败，与项目依赖无关（连 `pip install numpy` 都装不了）；容易误判成 Python 3.14 的版本问题，甚至想降级 Python。",
          ""
        ],
        "原因": [
          "",
          "Ubuntu 26.04 等 Debian 系系统默认开启 **PEP 668**：系统 Python 被标记为 \"externally managed\"，pip 拒绝直接向系统环境安装第三方包，防止第三方包覆盖 apt 管理的系统包、搞坏系统 Python。这不是 Python 3.14 的 bug，而是发行版默认策略（自 Ubuntu 23.04 起默认开启）。",
          ""
        ],
        "解法": [
          "",
          "1. **长期正确姿势（推荐）**：建虚拟环境，之后正常 `pip install` 不再报错：",
          "   ```bash",
          "   python3 -m venv .venv",
          "   source .venv/bin/activate",
          "   pip install -r requirements.txt",
          "   ```",
          "   提示：若报缺 ensurepip，先 `sudo apt install python3-venv`（精简镜像常见）。",
          "2. **临时绕过（单次救急）**：`pip install <包> --break-system-packages` —— 跳过 PEP 668 检查；代价是可能覆盖系统包、与 apt 冲突，不推荐长期使用。",
          "3. **系统级/命令行工具**：优先 `apt install python3-<包名>`；命令行应用用 `pipx install <应用>`（pipx 自动管理独立虚拟环境）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，Python 3.14.4 / pip 25.1.1，与文章环境一致）：",
          "  - 系统 pip 直接 `pip3 install six` → 完整复现 `error: externally-managed-environment`，提示可用 `--break-system-packages` 覆盖；",
          "  - `pip3 install --dry-run --break-system-packages six` → 正常解析并输出 `Would install six-1.17.0`（临时绕过路径生效，dry-run 未实际安装）；",
          "  - `python3 -m venv /tmp/venv-test` 后 venv 内 `pip install six` → 安装成功（长期方案生效）；测后已清理临时目录。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/pep668-externally-managed",
        "agent": "https://trove.xin/agent/problems/pep668-externally-managed"
      }
    },
    {
      "id": "pip-mirror-source",
      "title": "pip 安装超时/下载慢（ReadTimeoutError / WARNING: Retrying ... Read timed out）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "python",
        "pip",
        "deepcode",
        "code-review-graph",
        "logfire",
        "agent-framework"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/11785",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "国内网络（校园网/家庭宽带，无代理）跑 pip 时反复超时：`ReadTimeoutError: HTTPSConnectionPool(host='download.pytorch.org'...): Read timed out`，重试几轮后失败。你以为是包的问题，其实是**pip 默认从官方源下载，而官方源对国内网络没有稳定加速**——换源比等重试可靠得多。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "pip 默认从 `pypi.org` / `files.pythonhosted.org` 下载，这些域名对国内网络没有国内 CDN，直连经常被干扰或丢包；torch 类大包走 `download.pytorch.org`，情况更糟。官方对这类 issue 的回复往往是「internet connection issue because of China」（sd-webui#11785 原话）。",
          "",
          "同族但根因不同（别在本页死磕网络）：",
          "",
          "- `Hash mismatch`（下载后校验和不符）→ 镜像站同步延迟，换另一个镜像（阿里/腾讯）或 `pip install --no-cache-dir` 重试（机制核对：镜像缓存不同步）；",
          "- `no space left on device`（下载/安装阶段）→ 磁盘满；WSL 下 df 有空间但写不进是宿主盘满，先查 `df -h /mnt/c`。不同网络（校园网/家宽/公司）表现差异很大，所以**换国内镜像源**是最通用的解法。",
          "",
          "> 实测环境：校园网无代理，2026-08-29。",
          "",
          "pip 的下载是「index 寻址 + 文件分发」两段：元数据从 index 域名（默认 pypi.org）拉，wheel/sdist 从文件托管域名（files.pythonhosted.org，按内容重定向到 CDN）拉。国内网络对这两个域名没有稳定的国内 CDN 节点，丢包/重置发生在传输层——报错文案（timeout/connection reset）与包无关。",
          "",
          "镜像源（清华/阿里）是把 index 和文件托管一起镜像到国内节点，**换源 = 换寻址入口，包的校验和不变**。",
          "",
          "确定性推论：`pip install` 卡住先确认是网络层（`curl -I https://pypi.org` 测 index、直接下同一个 wheel 测文件域名），再换源；`-i` 临时换源 vs 写进 `~/.pip/pip.conf` 持久换源效果相同；大包（torch）优先下载 wheel 文件再本地装，避免大文件传输中断重来。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：清华源临时换源安装（想长期生效看一步步来）",
          "pip install <包> -i https://pypi.tuna.tsinghua.edu.cn/simple",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是源的问题 — **必做**",
          "",
          "报错里的 host 是 `pypi.org` / `files.pythonhosted.org` / `download.pytorch.org` 之一，且 `curl https://pypi.org` 也慢 → 就是源的问题。",
          "",
          "### 2. 临时换源装一次 — **必做**",
          "",
          "```bash",
          "pip install <包> -i https://pypi.tuna.tsinghua.edu.cn/simple",
          "```",
          "",
          "常用镜像：清华 `https://pypi.tuna.tsinghua.edu.cn/simple`、阿里 `https://mirrors.aliyun.com/pypi/simple/`、腾讯 `https://mirrors.cloud.tencent.com/pypi/simple`。",
          "",
          "### 3. 长期生效：写进 pip 配置 — **必做**",
          "",
          "```bash",
          "pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple",
          "```",
          "",
          "### 4. 信任主机问题（旧版 pip） — **必做**",
          "",
          "老镜像报 `untrusted host` 时补 `--trusted-host pypi.tuna.tsinghua.edu.cn`（新版已不需要）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "pip download six -i https://pypi.tuna.tsinghua.edu.cn/simple -d /tmp/pip-test",
          "```",
          "",
          "不再报超时、能正常下载即成功（实测：换源后安装/下载正常；官方源直连则 ReadTimeoutError 复现）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 校园网/家宽（无代理） | 官方源高发超时，换清华/阿里/腾讯源 |",
          "| 公司内网 | 有时有内部 PyPI 镜像，优先问运维 |",
          "| torch 等大包 | 走 download.pytorch.org 更慢，建议先换源再装 |",
          "| 有稳定代理 | 走代理直连官方源也可，但换源更简单 |",
          "| 弱网小包 | `pip install --timeout 60 --retries 5` 调大超时/重试也能装上小包；大包还是换源 |",
          "| 依赖树大的 meta 包（agent-framework） | 不带参数可能 CLOSE-WAIT 半开连接挂死、长时间无进展；带 `--timeout 30 --retries 5` 自动重试通过（2026-08-29 实测） |",
          ""
        ],
        "症状": [
          "",
          "国内网络（校园网/家庭宽带，无代理）跑 pip 时反复超时：",
          "",
          "```",
          "pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='download.pytorch.org', port=443): Read timed out.",
          "WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError(...)': /simple/pyyaml/",
          "```",
          "",
          "特征：报错里的 host 是 `pypi.org`/`files.pythonhosted.org`/`download.pytorch.org` 之一；",
          "装任何包都可能触发，重试几轮后失败；`curl https://pypi.org` 大概率也慢。",
          "官方对这类 issue 的回复往往是「internet connection issue because of China」（sd-webui#11785 原话）。",
          ""
        ],
        "原因": [
          "",
          "pip 默认从官方源下载，而官方源对国内网络没有稳定加速（pypi.org / files.pythonhosted.org 无国内 CDN，",
          "直连经常被干扰或丢包）；torch 类包走 `download.pytorch.org`，情况更糟。",
          "同一时间不同网络差异很大：校园网、家宽、公司网表现完全不同，所以「换源」比「等重试」可靠。",
          ""
        ],
        "解法": [
          "",
          "1. **一次性换源**：`pip install <包> -i https://pypi.tuna.tsinghua.edu.cn/simple`",
          "2. **长期生效**：`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple`",
          "3. 旧版 pip 补 `--trusted-host`；大包优先换源再装。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，校园网无代理）：官方源直连超时复现；清华源换源后安装正常。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「同根（国内换源家族）」读：",
          "",
          "- [npm install 慢/挂起](npm-mirror-registry.html)（同根：换源家族，node 侧）",
          "- [Go 模块拉取慢/超时](go-proxy-cn.html)（同根：换源家族，go 侧）",
          "- [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（同根：换源家族，容器侧）",
          "- [GitHub 直连慢/失败](github-direct-connect-fail.html)（网络层同根：出口不稳，一为 pip 一为 git）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/pip-mirror-source",
        "agent": "https://trove.xin/agent/problems/pip-mirror-source"
      }
    },
    {
      "id": "pip-ssl-cert",
      "title": "pip install 报 SSL 证书错误（CERTIFICATE_VERIFY_FAILED）——信任链断了，别急着 --trusted-host",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "pip"
      ],
      "evidence": "Evidence details kept locally (2026-09-01)",
      "source": "本站实测沉淀（2026-09-01）",
      "recorded": "2026-09-01",
      "sections": {
        "你会遇到什么": [
          "",
          "`pip install` 装包时突然报一长串：",
          "",
          "```",
          "ERROR: Exception:",
          "HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/xxx/",
          "SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1081)'))",
          "```",
          "",
          "更迷惑的是：**浏览器打开 PyPI 正常，curl 下载也正常，只有 pip 报证书错**；或者换了个国内源（清华/阿里）还是同一个错。常见场景：公司内网自建 PyPI 镜像（Nexus/devpi）、公司/校园网 SSL 拦截代理、私有仓库用了自签证书、旧证书过期。",
          "",
          "遇到它先别急着 `--trusted-host` 一把梭——那是跳过校验，等于把 HTTPS 的锁拆了，装了个「看起来成功」但可能被中间人替换的包。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "pip 走 HTTPS 下载包时，Python 的 ssl 模块会做**证书链校验**：服务器证书必须由某个「被信任的 CA」签发，且链上每一张证书都完整、未过期、域名匹配。校验失败时 pip 宁可报错也不下载——这是安全设计，不是 bug。",
          "",
          "「信任链断了」通常有三种形态：",
          "",
          "1. **自签证书**：服务器用的是自己签的证书（内网镜像、私有仓库常见），不在任何 CA 信任库里 → `self-signed certificate` / `unable to get local issuer certificate`；",
          "2. **公司代理 MITM**：公司网络用 SSL 拦截代理检查流量，代理给每个站点签发「代理自己的证书」——浏览器装了代理的 CA 所以正常，pip 用的是独立的证书库没装 → 换什么源都报同样的证书错（证书是代理签的，不是源的）；",
          "3. **证书过期/系统时间错**：报 `certificate has expired`。",
          "",
          "而 `--trusted-host` 的作用是**告诉 pip 别校验这个主机的证书**——不是修复，是跳过。实测（见验证证据）：加 `--trusted-host` 后 SSL 阶段直接放行。临时救急可以，长期用等于裸奔：HTTPS 的加密在，但「对方是不是真的 pypi.org」没人验证了。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 方案 A（临时救急，先装起来）：跳过单个主机的证书校验",
          "pip install --trusted-host <镜像域名> --index-url <镜像URL> <包名>",
          "# 持久化：pip config set global.trusted-host <镜像域名>",
          "",
          "# 方案 B（正规修复，推荐）：把 CA 导入系统信任库，pip 指向系统 bundle",
          "sudo cp 你的CA证书.crt /usr/local/share/ca-certificates/pipssl.crt",
          "sudo update-ca-certificates",
          "pip config set global.cert /etc/ssl/certs/ca-certificates.crt",
          "# 然后正常 pip install，不需要任何 --trusted-host",
          "```",
          "",
          "> 你的 CA 证书从哪来：内网镜像/自建仓库找管理员要（一般叫 `ca.crt` / `rootCA.pem`）；公司代理找 IT 要拦截证书；浏览器能打开的话，可以在浏览器「证书查看器」里导出。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认报错形态 — **必做**",
          "",
          "```bash",
          "pip install --index-url https://<你的源>/simple/ 某个包 2>&1 | tail -5",
          "# 看到 CERTIFICATE_VERIFY_FAILED / self-signed / unable to get local issuer 就是本页问题",
          "```",
          "",
          "顺手用 curl 看一眼服务器证书长啥样（区分「自签」还是「私有 CA」）：",
          "",
          "```bash",
          "echo | openssl s_client -connect <源域名>:443 -showcerts 2>/dev/null | grep -E \"subject=|issuer=\" | head -4",
          "# subject=CN=xxx 与 issuer=CN=xxx 相同 = 自签；issuer 是公司内部 CA = MITM/私有 CA",
          "```",
          "",
          "### 2. 判断属于哪种场景 — **必做**",
          "",
          "- 内网镜像/自建仓库 → 找管理员要 CA 证书（方案 B）；",
          "- 公司/校园网代理拦截 → 找 IT 要代理的 CA 证书；**换源没用**，因为证书是代理签的，换哪个域名都一样报错；",
          "- 报 `certificate has expired` → 先 `date` 看系统时间对不对，再考虑证书过期；",
          "- 浏览器正常、pip 报错 → 正是「pip 信任库没装 CA」的典型特征，方案 B 直接对症。",
          "",
          "### 3. 正规修复：CA 导入系统信任库 — **必做**",
          "",
          "```bash",
          "sudo cp ca.crt /usr/local/share/ca-certificates/pipssl.crt",
          "sudo update-ca-certificates   # 输出 \"1 added\" 即成功",
          "```",
          "",
          "### 4. 让 pip 用系统信任库 — **必做**",
          "",
          "```bash",
          "pip config set global.cert /etc/ssl/certs/ca-certificates.crt",
          "# 或环境变量：export PIP_CERT=/etc/ssl/certs/ca-certificates.crt",
          "# 或单次：pip install --cert /etc/ssl/certs/ca-certificates.crt ...",
          "```",
          "",
          "> 为什么必须做这步：pip 自带独立的 CA 包（certifi），默认不读系统信任库。CA 只进系统库不够，还要让 pip 指向它（实测：只 `update-ca-certificates` 不带 `--cert` 依旧报错）。",
          "",
          "### 5. 临时绕过只用于救急 — **可选**",
          "",
          "```bash",
          "pip install --trusted-host <镜像域名> --index-url <镜像URL> <包名>",
          "```",
          "",
          "仅限「内网测试环境、马上要装」时用；装完尽快走方案 B，并在心里记一笔：跳过校验期间装的包，不保证来源可信。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "# 1. 不带任何 --trusted-host / --cert 也能装（用公网源或你的源都行）",
          "pip install --index-url https://<你的源>/simple/ <包名>",
          "# 2. 正向验证证书链真的被信任了",
          "openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt 你的证书.crt",
          "# 输出 \"OK\" = 证书已被系统信任库接受",
          "```",
          "",
          "判定标准：**不加跳过参数、不报 SSL 错误、装包成功**，三条同时满足才算修好。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 表现与处理 |",
          "| Python 3.14 + IP 字面量源（https://127.0.0.1:port） | 报 `ValueError: check_hostname requires server_hostname`（2026-09-01 实测）：3.14 对 IP 字面量不发 SNI 导致 ssl 硬报错——源地址用域名（localhost 或内网域名），别用 IP |",
          "| Windows | CA 导入：`certutil -addstore Root ca.crt`（受信任的根证书颁发机构）；pip 指向：`pip config set global.cert C:\\path\\ca-bundle.crt` |",
          "| macOS | 证书导入「钥匙串-系统」并信任；pip 同用 `--cert` 指向导出的 bundle |",
          "| Docker 容器 | 容器默认不带宿主 CA，构建时 `COPY ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates`；运行时同样要 pip config |",
          "| 换源仍报证书错 | 大概率是代理 MITM：证书是代理签的，换域名没用，必须装代理的 CA（见「一步步来」第 2 步） |",
          "| 系统时间错乱 | 报 `certificate has expired`：先 `date` 校时（`sudo ntpdate` / `timedatectl`），再谈证书 |",
          ""
        ],
        "症状": [
          "",
          "`pip install` 报 `SSLError(SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate)` 或 `unable to get local issuer certificate` / `certificate has expired`；浏览器能打开包源、curl 正常，唯独 pip 失败；换源后错误不变。",
          ""
        ],
        "原因": [
          "",
          "HTTPS 的证书校验链路是「客户端信任库 → 服务器证书 → 逐级验签直到根证书」：pip 用自己捆绑的 CA 包（certifi），与浏览器/系统信任库是**三套独立列表**——这就是「浏览器能开、curl 能下、pip 报错」的机制解释。校验有三个独立条件：①链上每张证书都在信任库可追溯（自签证书在链顶端直接断裂 → `self-signed certificate`）；②证书未过期且系统时间正确（→ `certificate has expired`）；③主机名与证书 SAN 匹配。公司代理 MITM 时，代理对每个站点签发的都是「代理自己的证书」，所以**换源必然无效**——证书不是源的，是代理的。确定性判断路径：`openssl s_client -connect <host>:443 -showcerts` 看 `subject=`/`issuer=` 是否相同（自签）或 issuer 是否为内部 CA（MITM）；`--trusted-host` 是把「校验」整段短路，不是修复，装包前想清楚这一点。",
          "",
          "pip 的 HTTPS 证书链校验失败：服务器证书不是由 pip 信任库（certifi）里任何 CA 签发。三种来源：自签证书（内网镜像/私有仓库）、公司 SSL 拦截代理的私有 CA（换源无效）、证书过期/系统时间错。`--trusted-host` 是跳过校验不是修复。",
          ""
        ],
        "解法": [
          "",
          "1. **正规修复**：CA 证书导入系统信任库（`/usr/local/share/ca-certificates/` + `update-ca-certificates`），再让 pip 指向系统 bundle（`pip config set global.cert /etc/ssl/certs/ca-certificates.crt`）——实测 SSL 阶段通过。",
          "2. **临时绕过**：`pip install --trusted-host <域名> ...`，仅救急，长期等于关掉 HTTPS 身份校验。",
          "3. **场景分流**：内网镜像找管理员要 CA；公司代理找 IT 要拦截证书（换源无用）；`expired` 先校时。",
          "4. **Python 3.14 注意**：源地址用域名不用 IP 字面量（实测会触发 `check_hostname requires server_hostname`）。",
          ""
        ],
        "验证证据": [
          "",
          "- 2026-09-01 本机实测（WSL Ubuntu 26.04，Python 3.14.4，pip 25.1.1，自建 127.0.0.1:18443 HTTPS 源 + 自签 CA，SAN=DNS:localhost,IP:127.0.0.1）：`实测复现日志（2026-09-01）`",
          "  - 基线：`pip install --index-url https://localhost:18443/simple/ six` → `[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1081)`（实锤）；",
          "  - 快速绕过：`--trusted-host localhost` → SSL 阶段放行，后续报 `No matching distribution`（实锤：跳过校验生效）；",
          "  - 正规修复：CA 复制进 `/usr/local/share/ca-certificates/` + `update-ca-certificates` + `pip --cert /etc/ssl/certs/ca-certificates.crt` → SSL 阶段放行（实锤）；",
          "  - 负对照：撤销 CA + 不带 `--cert` → 回到 `CERTIFICATE_VERIFY_FAILED: self-signed certificate`（实锤：修复确实依赖信任库，不是缓存）；",
          "  - 变体：`https://127.0.0.1:18443`（IP 字面量）→ `ValueError: check_hostname requires server_hostname`（Python 3.14 新行为，实锤）。",
          ""
        ],
        "相关 canonical": [
          "",
          "- [pip 安装超时/慢（国内换源）](pip-mirror-source.html)——先换源再谈证书，换完源还报证书错就是本页问题",
          "- [设置了代理环境变量却不生效（大小写坑）](proxy-env-var.html)——代理通了但报证书错，多半是 MITM 证书没装",
          "- [JDK 老项目 TLS 协议升级](tls-jdk-upgrade.html)——TLS 层同族问题（协议交集/证书链），Java 侧版本"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/pip-ssl-cert",
        "agent": "https://trove.xin/agent/problems/pip-ssl-cert"
      }
    },
    {
      "id": "prompt-engineering-consensus",
      "title": "提示词工程——为什么你写了 500 字提示词，AI 还是不懂你？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个我闭着眼都能复述的画面：你把需求写成了 500 字的小作文，角色、背景、要求、注意事项全部安排上，满怀期待地发给 AI，结果它给你吐出一段\"看起来什么都说了，其实什么都没说\"的答案。你追问\"不对，我要的不是这个\"，它道歉、重来，然后给你一段换汤不换药的版本。你开始怀疑人生：是我没说清楚，还是这 AI 真的不行？",
          "",
          "还有更常见的一种：你跟 AI 说\"帮我写一个订单模块\"，它真的给你写了一个——用你根本没用过的框架、你项目里根本不存在的依赖、你完全没提过的假设。你问它为什么这么写，它说\"根据最佳实践\"。那一刻你想把它从屏幕上拽出来摇醒：**这个项目是 Java 8 + Spring Boot 2，不是你说的那个\"现代技术栈\"！**",
          "",
          "也有人在另一个极端：觉得提示词就是聊天，想到哪说到哪。结果同一个任务，上午跑出来一个样，下午跑出来一个样；改一个词，输出全变；不改词，输出也变。你没法复现、没法调试、没法把\"上次那个好结果\"稳定地再要一次。于是你觉得 AI 就是个碰运气的玩具，弃了。",
          "",
          "还有一群人走在中间：他们抄了一堆\"万能模板\"——什么\"你是一位资深专家\"\"请一步一步思考\"——但换到自己的场景就失效。模板里的角色是\"资深前端工程师\"，你的需求是\"帮我看一下这段 SQL 为什么慢\"；模板要求\"输出 Markdown 表格\"，你的下游程序要的是 JSON。抄来的模板像借来的西装，穿着不合身，又不知道哪里改。",
          "",
          "如果你正在经历这些——提示词越写越长 AI 越跑越偏、同一个任务每次结果都不一样、抄了模板不会改、或者干脆觉得\"提示词工程就是玄学\"——这篇文章是写给同类人的。它不是 Prompt 大全，是那些真把提示词当工程做过的人，用几千次无效对话换来的共识：**提示词不是聊天，是给 AI 写的接口文档——你有结构化、可复用、可迭代的输入，才有稳定、可靠、可预测的输出。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：为什么提示词这么难写？四个原因，每个都指向同一件事——**模糊的输入，必然得到概率的输出。**",
          "",
          "**第一，大模型是概率机器，不是搜索引擎。** 它的训练目标是\"预测下一个词\"，输出质量直接取决于输入提示词的清晰度与结构。你给它模糊的指令，它就只能做概率猜测——猜测你的业务背景、猜测你要的格式、猜测你没说的约束。猜得多了，\"幻觉\"和\"答非所问\"就是必然结果。不是它笨，是你给它的信息熵太高了。",
          "",
          "**第二，上下文不在提示词里，AI 就只能替你编。** 你的项目用什么技术栈、哪个文件是干嘛的、这个功能要兼容什么历史行为——这些你脑子里有，提示词里没有，AI 就只好凭文件名猜、凭模板经验改、凭局部页面判断全局规则。它\"看起来能跑\"，是因为它把所有你没说的假设都替你做了，而你的真实业务规则，恰恰藏在那些你没说的部分里。",
          "",
          "**第三，没有评估，就没有迭代。** 写代码你有一万种方式验证对不对：编译、测试、跑一遍看结果。写提示词呢？大多数人\"写完即用、用完即忘\"——这次好还是坏，为什么好为什么坏，没有任何记录。没有评估标准，就没有迭代依据，提示词水平就永远停在\"碰运气\"。",
          "",
          "**第四，把提示词当\"写作\"而不是\"工程\"。** 一次写一大段、每次都从头写、改全靠手感——这是写作思维。工程思维是：拆结构（角色/任务/约束/示例/输出）、建模板（可复用）、做版本（可对比）、定指标（可评估）。写作思维让提示词永远是一次性消耗品，工程思维才让它变成可积累的资产。**提示词工程的核心不是\"会说话\"，是\"把话说成可维护的系统\"。**",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：提示词 = 角色 + 任务 + 约束 + 示例 + 输出格式",
          "",
          "**五件套说全，AI 才不用猜。** 赶时间只看这一条：下次写提示词，先检查这五样缺不缺——角色（以谁的视角做）、任务（做什么）、约束（不能做什么/边界是什么）、示例（长什么样算对）、输出格式（交什么形状的东西）。下面经验 1~6 是为什么和怎么做。",
          "",
          "### 经验 1：分层写，别写小作文——上下文、任务、格式三件套",
          "",
          "现象：我见过最典型的失败提示词，是\"帮我写一个 Express 的错误处理中间件\"——一句话，什么都没说。AI 当然能写，但它会写一个\"通用版本\"：不知道你的项目是 JavaScript 还是 TypeScript、不知道你有没有自定义错误类、不知道你要不要日志、要不要测试。于是你拿到一个哪都不挨着的中间件，改的时间比自己写还长。",
          "",
          "反过来，也有人走向另一个极端：把 500 字小作文一次砸给 AI，背景、情绪、要求、历史、注意事项全搅在一起——AI 照样跑偏，因为信息挤成一团，它分不清哪个是约束、哪个是背景、哪个是任务。",
          "",
          "做法：把提示词分成清晰的三层，每层只说一件事：①**上下文（Context）**——项目背景和技术栈、当前文件的作用、相关代码片段；让 AI 知道\"你在什么世界里工作\"；②**任务（Task）**——具体要做什么、输入输出是什么、有什么约束条件；让 AI 知道\"你要它干什么\"；③**格式（Format）**——代码风格、注释规范、测试要求、输出结构；让 AI 知道\"交给你什么东西\"。",
          "",
          "有个更完整的版本叫 CRISP：Context（背景）+ Role（角色）+ Instruction（指令）+ Specificity（细节）+ Presentation（呈现）——角色回答\"以谁的视角做\"，指令回答\"做什么\"（要单一、具体、简洁、有上下文），细节回答\"做到什么程度\"（格式/长度/风格），呈现回答\"如何交付\"（JSON、Markdown、表格）。",
          "",
          "为什么：AI 解析结构化输入的能力远强于解析散文。分层的作用不是好看，是**把信息损耗率压下去**——同样的需求，无结构提示的信息损耗可能接近一半，结构化之后能降到很低：任务边界清晰了、专业度上来了、输出可控了。你每说清一层，AI 就少猜一层；它猜得越少，输出越稳。记住那个原则：**先给上下文，再给具体任务**——AI 和人都一样，不知道背景的任务，做出来必然四不像。",
          "",
          "### 经验 2：建模板库——把提示词当代码管理",
          "",
          "现象：有个人写了 200 多篇提示词之后总结出一个公式：\"角色 + 任务 + 约束 + 示例 + 输出格式\"。",
          "",
          "但更有价值的不是这个公式，而是他随后做的事：把常用场景的提示词固化成模板，存进项目的 `.ai-templates/` 目录——new-component.md、refactor-code.md、write-tests.md、debug-error.md、explain-code.md，每个模板留好占位符，用的时候填参数。",
          "",
          "从此他写提示词不是\"从零开始想\"，是\"打开模板填参数\"。",
          "",
          "做法：①**为高频场景建模板**——新建组件、重构代码、写测试、调试报错、解释代码，这些场景你每周都会遇到，每个建一个模板文件，角色、上下文、任务、要求、输出格式全部预设好，只留 `{变量}` 占位符；②**从抄模板到改模板**——新手先抄模板没问题，但要有意识分析模板的结构：为什么这个模板要设角色？为什么约束写在这里？",
          "",
          "分析三五个模板之后，你就能自己组装了；长期依赖别人的模板有三个副作用：满足不了个性化需求、用不到 AI 的深层能力、产出和所有人同质化；③**像管理代码一样管理提示词**——版本控制（改了什么、为什么改）、模板化（常用场景固化）、测试（用 A/B 对比不同版本的效果，建立量化指标）；",
          "",
          "④**每日复盘更新模板**——晚上花十分钟记录今天的 AI 使用心得，哪类提示词效果好、哪类总是翻车，更新到模板库里。",
          "",
          "为什么：写提示词最大的浪费是\"每次都从零开始\"。模板库把一次性的好提示词变成可复用的资产——你今天调好的一个模板，未来每次用都省一次试错。更重要的是，**模板化倒逼你思考结构**：你能把提示词固化成模板，说明你真的理解了它为什么有效；不能，说明你还在凭感觉。提示词和代码一样，是资产，不是消耗品。",
          "",
          "### 经验 3：迭代与评估——没有完美的提示词，只有不断调优的提示词",
          "",
          "现象：大多数人写提示词是一次性的：写完就用，用坏就重写。而真正把提示词当工程的人，会告诉你一句反直觉的话：**完美的提示词很少一蹴而就，它是试出来的。** 第一次输出不满意，不是失败，是数据——它告诉你哪里没说清楚。我有一个朋友接手一个用 AI 重构的活，第一次让 AI 改页面，AI 把整个按钮交互都换了，他回复\"这里不对\"，AI 又改了一版，还是不对。",
          "",
          "后来他把反馈写成约束：\"按钮位置必须与源项目保持一致\"\"这个入口需要保留历史行为\"——AI 立刻对了。",
          "",
          "**\"这里不对\"是情绪，不是约束；\"必须保持 X 一致\"才是约束。**",
          "",
          "做法：①**把反馈变成约束，而不是情绪**——\"这里不对\"\"这个不好看\"没有信息量；\"按钮位置必须与源项目一致\"\"字段必须保留\"\"这个流程不能简化\"才有；每次反馈，都把它沉淀成下一轮的显式约束；②**迭代要有记录**——同一任务的提示词改了三版，每版输出了什么、哪里不行，记下来；没有记录，你就是在同一个坑里反复跳；③**用 A/B 对比评估**——两个版本提示词各跑几次，对比输出质量；",
          "",
          "质量怎么量化？",
          "",
          "定义你的指标：格式正确率、首次通过率、返工次数、人工修改量；④**给 AI 设\"确认闸门\"**——遇到高风险决策（鉴权流程、权限模型、数据契约、是否引入新依赖），要求 AI 先停下来给 2-3 个方案、等确认，而不是让它替你拍板；⑤**要求 AI 先读、先问、先设计**——复杂任务的开局提示词不是\"开始做\"，是\"先读上下文，整理出事实清单，在完成这一步之前不要修改任何文件\"；",
          "",
          "先让它证明自己看过上下文，再让它动手。",
          "",
          "为什么：提示词的质量不是写出来的，是迭代出来的。**没有评估就没有迭代，没有迭代就没有进步**——这条对提示词和对代码一样成立。把反馈变成约束，是把\"人机对话\"变成\"人机协作\"的关键一步：情绪化的反馈只能让 AI 猜，结构化的约束能让它校准。\"确认闸门\"则把 AI 从\"执行者\"变成\"调查员\"——它先证明自己懂，再动手，防止它在关键位置替你做业务决策。",
          "",
          "### 经验 4：复杂任务拆着做——设计先行，地基先打，一次只搬一块",
          "",
          "现象：很多人第一次让 AI 做复杂任务，习惯把需求压缩成一句话：\"帮我把这个项目重构一下。\"这句话能启动对话，但很难启动一场可靠的工程协作。AI 最容易出的问题不是不会写代码，是**太快开始写代码**：凭文件名猜业务含义，凭模板经验改接口，凭局部页面判断全局规则，最后得到一个\"看起来能跑，但关键契约已经变了\"的结果。",
          "",
          "一个大型重构，如果让 AI 一次性把所有页面搬过去，你会发现搬着搬着，请求协议不一致、权限判断不一致、数据结构不一致、路由规则不一致——全部返工。",
          "",
          "做法：①**先写设计文档，再写代码**——要求 AI 先输出重构设计文档：保留什么、迁移什么、哪些规则以接口文档为准、参考实现能复用哪部分、风险清单、分阶段计划；设计文档通过之前，不修改业务代码；②**先做地基，再搬业务**——请求封装、鉴权状态、权限导航、分页协议、通用组件，这些基础设施先落地，再用一个低风险模块当样板，跑通之后才大规模迁移；",
          "",
          "③**一次只迁移一个模块，且三线对齐**——视觉交互向新项目靠拢、业务字段向源项目靠拢、数据契约向接口文档靠拢，三条线缺一不可；",
          "",
          "发现冲突就让 AI 停下来说明，不要自行选择；④**差异审查比\"我完成了\"重要**——每批改完，要求 AI 按源项目做差异审查：列表字段、操作流程、权限判断、详情展示，逐项核对，先不改代码，只报差异；⑤**复杂推理用思维链**——让 AI 分步思考：\"请一步步分析，先写步骤，再给答案\"；给一个带推理过程的示例，比给十个干巴巴的结果管用。",
          "",
          "为什么：复杂任务的失败模式永远是\"一次性做完\"，成功模式永远是\"分批做、批批验证\"。**AI 的速度是优势，但前提是你给它画好边界**——设计文档是边界，地基是边界，三线对齐是边界，差异审查是边界。没有边界的 AI 重构，是把速度变成风险放大器；有边界的 AI 重构，才是真正的效率杠杆。思维链则利用了模型\"先推理后作答\"的能力——你把推理过程要过来，就能看到它哪里想错了，而不是只看到错误结论。",
          "",
          "### 经验 5：知道提示词的边界——它决定\"怎么想\"，不决定\"能做什么\"",
          "",
          "现象：有人用纯提示词让 AI\"查一下北京的天气\"，AI 回答\"抱歉，我无法实时查询\"。这不是提示词写得不好，是**能力边界**问题：提示词只能调用模型知识库里的东西，查不了实时数据。还有人遇到更隐蔽的边界问题：让 AI 写代码，它写出来了，但用的是你没见过的依赖、没验证过的 API——因为它的\"知识\"截止在某个时间点，新版本的库它可能根本不知道。提示词再完美，也突破不了这些边界。",
          "",
          "做法：①**分清\"怎么想\"和\"能做什么\"**——提示词决定 AI 怎么想（角色、思路、风格），工具/Skills 决定 AI 能做什么（查天气、发博客、操作浏览器、调用 API）；需要实时数据、外部操作、专有接口时，别只写提示词，要给它配工具；②**记住优先级：提示词优先于微调**——能靠提示词解决的问题，不要先上微调：写提示词成本低、可解释、可迭代；",
          "",
          "如果提示词都达不到基本要求，微调成功的可能性也很低；",
          "",
          "反过来提示词达标后，微调可以再进一步；③**AI 生成的代码必须过审查**——AI 写代码很快，但它对\"你的系统\"的理解永远是局部的；关键逻辑、安全边界、性能细节必须人审，样板代码和重复逻辑可以交给 AI；④**别把 AI 当最终答案**——它是\"第二大脑\"，不是\"外包大脑\"：架构决策、边界条件、业务约束这些，永远是人的责任。",
          "",
          "为什么：**所有工具都有能力边界，提示词也不例外。** 知道边界在哪里，比知道技巧更重要——在边界内使劲，是提效；在边界外使劲，是浪费。提示词与工具的正确关系是互补：提示词把\"要什么\"说清楚，工具把\"能做到什么\"补齐。而\"AI 生成必须人审\"这条，是所有 AI 编程经验里最不性感但最保命的一条：AI 的正确率再高，在关键路径上，它的错误率也是你无法承受的。",
          "",
          "### 经验 6：人是最终负责人——AI 负责样板，你负责架构",
          "",
          "现象：有个使用 Copilot、Cursor、Codex、Claude Code 等一堆工具、累计生成超过 50 万行代码的人，总结出一个反直觉的结论：**真正让效率提升的不是工具本身，而是一套可复用的工作流。** 他的工作流是：AI 生成代码骨架 → 人手写核心逻辑和边界条件 → AI 补充单元测试 → AI 做代码审查 → 人工最终确认。",
          "",
          "他踩过最大的坑，是让 AI 从零开始写整个功能——代码能跑，但难以维护，调试时间远超手写。",
          "",
          "做法：①**人机分工要明确**——AI 负责样板代码、重复逻辑、测试生成、PR 描述；人负责架构、边界条件、性能、安全；②**别让 AI 从零写整个功能**——从零开始意味着 AI 替你做了所有架构决策，而它对你的业务一无所知；正确姿势是\"骨架给 AI，血肉自己填\"；③**关键路径必须人审**——AI 生成的代码要当\"实习生代码\"看：值得复用，但必须 review；",
          "",
          "线上 bug 的排查成本，永远高于写代码时多花的那五分钟；",
          "",
          "④**把 AI 当协作者，不是生成器**——让它先读、先问、先设计，再分批改、分批验证；它越了解上下文，产出越接近你要的；⑤**沉淀工作流，而不只是提示词**——晨间规划、功能开发、代码审查、晚间复盘，把 AI 嵌进流程的固定环节，而不是想起来才用。",
          "",
          "为什么：**工具放大的是人的能力，不是替代人的判断。** AI 生成代码的速度是人的十倍，但\"什么是好代码\"的判断力，仍然是人的。把架构和边界交给 AI，等于把方向盘交给一个从没开过这条路的人——它可能开得很快，但拐错一个弯，返工成本远超省下的时间。分工明确的团队效率高，人机协作也一样：各干各擅长的事，才是杠杆；让 AI 干你不放心的事，是赌。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. **今天**：把你最常用的一个场景（比如\"写测试\"或\"解释代码\"）写成一个模板，存进项目的 `.ai-templates/`；下次直接用，用坏了就改模板，不改正文；",
          "2. **本周**：每次写提示词前检查五件套——角色、任务、约束、示例、输出格式，缺哪个补哪个；复杂任务一律拆成\"先读上下文 → 确认方案 → 分批做 → 批批审查\"四步；",
          "3. **每次反馈**：把\"这里不对\"改成\"必须保持 X 一致\"；把新约束写进下一轮提示词，而不是口头纠正；",
          "4. **每个任务结束**：记一行评估——这次用了什么提示词、输出质量几分、下次怎么改；攒够 10 条记录，你会看到自己的提示词水平曲线；",
          "5. **长期**：像管理代码一样管理提示词——模板进版本库、版本做对比、效果定指标；AI 生成的代码永远过审查，关键逻辑永远人写。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人日常（写文章/学习/查资料）**：用模板库管你的常用提示词——写作模板、总结模板、翻译模板各一个；把\"请一步步分析\"变成肌肉记忆；每次输出不满意，先改提示词再改脾气。",
          "- **开发者（AI 辅助编程）**：三层结构 + 模板库 + 审查纪律三件套；复杂重构必须设计先行、确认闸门、分批验证；AI 生成代码当实习生代码 review，关键路径人写。",
          "- **团队（多人用 AI）**：把提示词模板收进仓库，团队共用；新人入职先看模板库，而不是自己从零摸索；定期做提示词分享，把个人经验变成团队资产。",
          "- **API 集成（提示词进产品）**：输出格式必须强制指定（JSON Schema 级别）；用 A/B 测试对比不同版本；建立量化评估指标（格式正确率、首次通过率）；像管理代码一样做版本控制和回滚。",
          "- **学生/入门者**：从抄模板开始没问题，但每周分析一个模板\"为什么这样写\"；三个月后你会发现自己能组装了；记住：提示词是技能，不是天赋——它靠练习和复盘，不靠灵感。",
          "- **如果你正卡在\"AI 总是不听话\"**：从最小的闭环开始——今天把五件套写全一个提示词；明天建一个模板；本周给每个任务记一行评估。提示词工程不是玄学，是手艺：手艺靠的是反复练，不是一次顿悟。",
          "",
          "收尾：写了几年提示词，我最深的体会是：五百字的小作文，不如五行的结构。AI 不需要你的文学才华，需要你的工程纪律——角色、任务、约束、示例、输出格式，五件套摆齐，比任何修辞都管用。把常用提示词收进模板库、像代码一样管版本，你的输出质量会稳定到连你自己都惊讶。最后永远记住：AI 负责样板，你负责架构。输出不对，先改自己的输入，别急着骂模型。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/prompt-engineering-consensus",
        "agent": "https://trove.xin/agent/problems/prompt-engineering-consensus"
      }
    },
    {
      "id": "proxy-env-var",
      "title": "设置了代理环境变量却不生效——有的命令走代理有的不走（HTTP_PROXY 大小写坑）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "curl"
      ],
      "evidence": "Evidence details kept locally (2026-09-01)",
      "source": "本站实测沉淀（2026-09-01）",
      "recorded": "2026-09-01",
      "sections": {
        "你会遇到什么": [
          "",
          "你在 `.bashrc` 里 `export HTTP_PROXY=http://127.0.0.1:7890`，结果 `curl` 还是直连超时、`git` 还是慢、`pip install` 还是报错——**明明设了代理，有的命令就是不听话**。更迷惑的是：同一个代理变量，`pip` 走了、`curl` 不走，或反过来。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "不同工具对代理环境变量的读取契约不一样，这是「有的走有的不走」的机制根因：curl 故意忽略大写 `HTTP_PROXY`（CGI 环境下大写变量可能被外部注入、可被恶意设置的旧安全设计），只认小写 `http_proxy`/`https_proxy`，`HTTPS_PROXY` 大写生效是官方唯一特批；`pip`/`git` 认大写（也认小写）；",
          "",
          "`npm` 以 `npm config get proxy` 配置优先。",
          "",
          "`NO_PROXY` 是第二契约：匹配的域名/地址直接绕过代理，缺了它本地请求会被塞进代理。确定性验证路径是「死端口反向验证」：把代理临时指向不存在的端口（如 `http://127.0.0.1:1`），报连接失败 = 该工具确实读了变量；不报错 = 大小写不对/该工具不读这个写法——比看日志快得多。",
          "",
          "**不同的工具对代理环境变量的「大小写敏感度」不一样**，这是所有混乱的根源（2026-09-01 本机实测，curl 8.18 / pip 25.1）：",
          "",
          "- **curl 只认小写** `http_proxy` / `https_proxy`：它**故意忽略大写 `HTTP_PROXY`**（安全设计——CGI 环境里大写变量可能被外部注入，防止代理被恶意设置）。`HTTPS_PROXY` 是大写却生效的**唯一特例**（curl 官方特批）。",
          "- **pip 认大写** `HTTP_PROXY` / `HTTPS_PROXY`（也认小写）。",
          "- **git 认大写**（也认小写 `http.proxy` 配置项）。",
          "- **npm 认小写 + 大写都有**，但以 `npm config get proxy` 配置优先。",
          "",
          "所以「export 一个大写 HTTP_PROXY」时：`pip` 走了代理，`curl` 无视它直连——这就是「有的走有的不走」的直接原因。",
          "",
          "**第二个坑是 `NO_PROXY`**：设了代理后本地/内网请求（`localhost`、公司内网、`127.0.0.1`）也可能被塞进代理导致失败，需要 `NO_PROXY=localhost,127.0.0.1,内网域名` 显式绕过（实测：`http_proxy=死端口 + NO_PROXY=127.0.0.1` 时 curl 直连成功，去掉 NO_PROXY 立即失败）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：四种变量全设小写+大写，并配好 NO_PROXY（在 ~/.bashrc 里加这几行）",
          "export http_proxy=http://127.0.0.1:7890",
          "export https_proxy=http://127.0.0.1:7890",
          "export HTTP_PROXY=http://127.0.0.1:7890",
          "export HTTPS_PROXY=http://127.0.0.1:7890",
          "export NO_PROXY=localhost,127.0.0.1,.local",
          "",
          "# 验证：确认真的走代理了（看请求耗时/出口 IP，或临时把代理指向死端口看是否报错）",
          "env | grep -i proxy            # 四种变量 + NO_PROXY 都在",
          "curl -I --max-time 5 https://example.com   # 成功且不超时 = 代理生效",
          "```",
          "",
          "> 验证技巧：临时 `http_proxy=http://127.0.0.1:1 curl https://example.com`——**报代理连接失败 = 代理变量生效**（变量被读了）；不报错 = 变量没被这个工具读（大小写不对）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认代理端口真的在监听 — **必做**",
          "",
          "`ss -ltn | grep 7890`（或你代理软件的端口）。代理软件没起，设啥变量都没用。",
          "",
          "### 2. 逐工具验证 — **必做**",
          "",
          "```bash",
          "http_proxy=http://127.0.0.1:1 curl -s --max-time 3 http://example.com; echo \"curl: $?\"",
          "HTTP_PROXY=http://127.0.0.1:1 pip3 download --no-deps -d /tmp/t six; echo \"pip: $?\"",
          "```",
          "",
          "curl 返回非 0 = 小写生效；pip 失败 = 大写生效。",
          "",
          "### 3. git 单独查 — **必做**",
          "",
          "`git config --global --get http.proxy`——git 除了环境变量还认 `http.proxy` 配置，两者都有时配置优先。",
          "",
          "### 4. 代理只对部分域名生效 — **必做**",
          "",
          "公司/校园网代理常有白名单：用 `NO_PROXY` 排除内网，用 `ALL_PROXY`（socks5 场景）兜底。",
          "",
          "### 5. 容器/服务里不继承 — **必做**",
          "",
          "Docker 容器默认不继承宿主代理环境变量，需要 `docker run -e http_proxy=...` 或 compose `environment:` 显式传。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "# 1. 环境变量四件套 + NO_PROXY 都在",
          "env | grep -i proxy",
          "# 2. 真实请求成功且快（走代理 vs 直连的耗时差异一眼可见）",
          "curl -o /dev/null -s -w 'time=%{time_total}s",
          "' --max-time 8 https://example.com",
          "# 3. 反向验证（代理真的被读取）：指向死端口应立刻失败",
          "http_proxy=http://127.0.0.1:1 curl -s -o /dev/null --max-time 3 http://example.com && echo \"BAD: 没走代理\" || echo \"OK: 代理变量生效\"",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 表现 |",
          "| Windows（PowerShell） | 大小写不敏感，`$env:HTTP_PROXY` 与 `$env:http_proxy` 等价；curl.exe / git 行为与 WSL 一致（curl.exe 同样忽略大写 HTTP_PROXY） |",
          "| macOS | 与 Linux 完全一致（curl 同款安全设计） |",
          "| 代理软件自带系统代理 | Clash/V2Ray「系统代理」改的是系统级设置（Windows 代理开关/`/etc/environment`），不写 shell 环境变量——`env | grep proxy` 为空是正常，命令走系统代理不依赖变量 |",
          ""
        ],
        "症状": [
          "",
          "设了代理环境变量但表现不一致的典型形态：",
          "",
          "- `curl` 仍然直连超时/失败，而 `pip install` 却能走通——同一个变量，有的命令听、有的不听；",
          "- 只 `export HTTP_PROXY=...`（大写）时 `curl` 完全无视，`git`/`pip` 却生效；",
          "- 设了代理后 `localhost`、`127.0.0.1`、内网请求反而失败——被代理截走（NO_PROXY 缺失）；",
          "- 容器/服务里设了变量却依旧直连——环境变量没被传进容器。",
          "",
          "同族但别在本页死磕：报错明确指向「代理服务器拒绝连接」时，先确认代理软件本身在监听（`ss -ltn`），再回来看变量大小写。",
          ""
        ],
        "原因": [
          "",
          "不同工具对代理环境变量的「大小写敏感度」不一样：**curl 故意忽略大写 `HTTP_PROXY`**（CGI 环境下大写变量可能被外部注入的安全设计，`HTTPS_PROXY` 是唯一特批生效的大写），只认小写 `http_proxy`/`https_proxy`；`pip`/`git` 认大写（也认小写）；`npm` 以 `npm config get proxy` 配置优先。所以「只设一个大写 HTTP_PROXY」时，curl 直连、pip 走代理——「有的走有的不走」是设计使然，不是配置写错。第二个坑是 `NO_PROXY` 缺失：本地/内网请求被塞进代理导致失败。",
          ""
        ],
        "解法": [
          "",
          "1. **四件套全设 + NO_PROXY**（一键命令见「快速修复」）：小写+大写四个变量都 export，并配 `NO_PROXY=localhost,127.0.0.1,.local`——覆盖所有工具的大小写偏好，一劳永逸。",
          "2. **逐工具反向验证**（见「快速修复」）：临时指向死端口，报错 = 变量被读；不报错 = 这个工具不认这个写法。",
          "3. **git 单独查**：`git config --global --get http.proxy`——git 除了环境变量还认 `http.proxy` 配置，两者都有时配置优先。",
          "4. **容器/服务显式传**：Docker 默认不继承宿主变量，`docker run -e` 或 compose `environment:` 补上。",
          "5. **系统代理模式不冲突**：Clash/V2Ray 的「系统代理」不写 shell 变量，`env | grep proxy` 为空属正常，不用重复设。",
          ""
        ],
        "验证证据": [
          "",
          "- 2026-09-01 本机实测（WSL Ubuntu 26.04，curl 8.18 / pip 25.1）：`实测复现日志（2026-09-01）`",
          "  - 基线直连 127.0.0.1:8089 → 200；大写 `HTTP_PROXY=死端口` 下 curl 仍 200（忽略大写，实锤）；",
          "  - 小写 `http_proxy=死端口` → curl 000（小写生效，实锤）；",
          "  - `HTTPS_PROXY=死端口` + https 目标 → 000（HTTPS_PROXY 大写生效，实锤）；",
          "  - `http_proxy=死端口 + NO_PROXY=127.0.0.1` → 200（NO_PROXY 绕过，实锤）；",
          "  - pip 在大写 HTTP_PROXY=死端口下报 `Could not find a version`（pip 认大写，实锤）；加 `NO_PROXY=pypi.org` 后下载成功。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/proxy-env-var",
        "agent": "https://trove.xin/agent/problems/proxy-env-var"
      }
    },
    {
      "id": "python-command-not-found",
      "title": "python: command not found——Ubuntu 只有 python3，没有 python 命令",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/39885520/why-does-ubuntu-show-python-command-not-found",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "在 Ubuntu/Debian（含 WSL）里执行 `python --version` 报 `Command 'python' not found`，你以为是 Python 没装，去搜教程重装，结果 `python3 --version` 明明正常——问题不是没装 Python，而是**系统里根本没有 `python` 这个命令**。",
          "",
          "老教程、Windows 习惯写死的 `python` 一跑就挂。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Ubuntu 18.04 起不再提供 `python` 命令（只装 `python3`），这是**官方设计**：刻意避免 python2/python3 混淆，防止脚本拿到错误的解释器。但大量老教程、CI 配置和 Windows 用户习惯仍写 `python`，于是\"命令找不到\"成了国内新手最常撞的墙之一。",
          "",
          "- 直接执行 `python` 走的是系统 PATH 查找，Ubuntu 没装 `python-is-python3` 就没有这个入口。",
          "- `python3` 存在 ≠ `python` 存在，两者是独立的命令名。",
          "",
          "> 实测环境：WSL Ubuntu，2026-08-29，Python 3.14.4。",
          "",
          "`python` 与 `python3` 是两个独立的命令名，PATH 查找只认名字不认「同义」：Ubuntu 18.04 起发行版刻意不提供 `python` 入口，避免脚本拿到错误版本的解释器（python2 时代的历史教训）——没有 `python-is-python3` 这个包时，系统里根本没有名为 `python` 的可执行文件，shell 报 command not found 而不是「版本不对」。",
          "",
          "确定性推论：报错在 Ubuntu/WSL 上先 `which python3` 确认解释器在，再决定——脚本/CI 里把 `python` 改成 `python3`（正解，符合发行版设计），或 `sudo apt install python-is-python3`（提供 `python` → `python3` 的符号链接，兼容老脚本）；",
          "",
          "别用 alias（alias 不进非交互 shell，CI 里不生效）；判断路径固定：`command -v python` 无输出 = 入口不存在，不是坏了。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：装 python-is-python3，让 python 指向 python3（官方包，无副作用）",
          "sudo apt update && sudo apt install -y python-is-python3 && python --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先确认是不是这个问题 — **必做**",
          "",
          "```bash",
          "command -v python; echo \"exit=$?\"",
          "python3 --version",
          "```",
          "",
          "`command -v python` 无输出、而 `python3 --version` 正常 → 就是这个问题（实测一致）。",
          "",
          "### 2. 装 python-is-python3（推荐） — **必做**",
          "",
          "```bash",
          "sudo apt update && sudo apt install -y python-is-python3",
          "```",
          "",
          "这是 Ubuntu 官方包，装上后 `python` 就是 `python3` 的软链，不影响任何现有环境。",
          "",
          "### 3. 不想装的话，改写法 — **必做**",
          "",
          "- 脚本/命令里用 `python3`；",
          "- shebang 写 `#!/usr/bin/env python3`（兼容性最好，不要依赖 `python`）；",
          "- 或当前 shell 临时 `alias python=python3`（只对本 shell 生效）。",
          "",
          "> venv 场景不用管：进入 venv 后 `python` 本来就正常，问题只出现在系统解释器。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "python --version",
          "```",
          "",
          "能正常输出版本号（如 `Python 3.14.4`）即成功。实测：修复前 `python --version` 报 not found（exit=127），`python3 --version` → Python 3.14.4。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Ubuntu/Debian 18.04+ | 无 `python` 命令，装 `python-is-python3` 补齐 |",
          "| WSL Ubuntu | 同上；Windows 侧习惯 `python` 的注意这是 Linux 侧行为 |",
          "| venv 虚拟环境 | venv 自带 `python`，无需处理 |",
          "| macOS | 自带 `python3`，`python` 视 Xcode CLT 版本而定，同样建议写 `python3` |",
          ""
        ],
        "症状": [
          "",
          "在 Ubuntu/Debian 系（含 WSL）里执行 `python` 直接失败：",
          "",
          "```",
          "$ python --version",
          "Command 'python' not found, did you mean:",
          "  command 'python3' from deb python3",
          "  command 'python' from deb python-is-python3",
          "```",
          "",
          "- `python3` 明明可用（`python3 --version` 正常）。",
          "- 脚本/教程里写死 `python`（老教程、Windows 习惯）→ 一跑就挂。",
          ""
        ],
        "原因": [
          "",
          "1. **Ubuntu 18.04+ 起不再提供 `python` 命令**（只装 `python3`），刻意避免 python2/python3 混淆——这是官方设计，不是装坏了。",
          "2. 大量老教程、脚本、CI 配置仍写 `python`；Windows 用户习惯 `python` 也指 python3。",
          ""
        ],
        "解法": [
          "",
          "1. **临时/脚本内**：把命令改成 `python3`；或 `alias python=python3`（仅当前 shell）。",
          "2. **系统级补上 python 命令（推荐）**：",
          "   ```",
          "   sudo apt update && sudo apt install -y python-is-python3",
          "   python --version   # 现在指向 python3",
          "   ```",
          "3. **写脚本用 `python3` 或 shebang `#!/usr/bin/env python3`**——兼容性最好，不要依赖 `python`。",
          "4. venv 场景：进入 venv 后 `python` 正常（venv 自带）；问题只出现在系统解释器。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机（WSL Ubuntu，2026-08-29）：",
          "- 实测 1：`command -v python` → 无输出，exit=1。",
          "- 实测 2：`python --version` → `python: command not found`，exit=127——与用户报错一致。",
          "- 实测 3：`python3 --version` → Python 3.14.4，exit=0（python3 存在）。",
          "- 实测 4：`python-is-python3` 未安装（修复命令如实给出，未安装实测）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/python-command-not-found",
        "agent": "https://trove.xin/agent/problems/python-command-not-found"
      }
    },
    {
      "id": "python-distutils-removed",
      "title": "Python 3.12+ 报 ModuleNotFoundError: No module named 'distutils'",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "python",
        "pip"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/aitaozi/p/18840999",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "换到 Python 3.12+（含 3.13/3.14）后，运行旧工具、激活新虚拟环境、或在 PyCharm 导入新解释器时直接报 `ModuleNotFoundError: No module named 'distutils'`。",
          "",
          "老项目/老工具链最常见：旧 setup.py、依赖 distutils 的旧库、以及默认不带 setuptools 的新建 venv（3.12 起 venv 只带 pip 不再带 setuptools）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Python 3.12 起 **distutils 从标准库移除**（此前已废弃多年）。",
          "",
          "同时 3.12 开始新建 venv 默认**只装 pip、不再预装 setuptools**，而 distutils 的替身（shim）恰好由 setuptools 提供——`import distutils` 实际映射到 `setuptools/_distutils`。",
          "",
          "**报错本质是「环境中没有 setuptools，也就没有 distutils 替身」。**",
          "",
          "| 环境 | import distutils |",
          "|---|---|",
          "| 系统 Python（Ubuntu 预装 setuptools） | ✅ 正常（实测加载 setuptools/_distutils 替身） |",
          "| 干净 venv（3.12+ 只带 pip） | ❌ ModuleNotFoundError（实测复现） |",
          "",
          "distutils 从 3.12 起从标准库移除，但真正的机制在「替身」：`import distutils` 实际上由 setuptools 的 `_distutils` 提供（setuptools 装一个 distutils shim 顶替）。",
          "",
          "3.12 起新建 venv 默认只装 pip、不预装 setuptools——所以「import distutils 失败」的本质是「环境里没有 setuptools，也就没有替身」。",
          "",
          "系统 Python 不报是因为发行版预装了 setuptools。",
          "",
          "确定性推论：报 `ModuleNotFoundError: No module named distutils` 先查环境里有没有 setuptools（`pip show setuptools`）；两条正路——装 setuptools（`pip install setuptools`，替身回归）或改用 `packaging`/标准库替代（`sysconfig`、`tomllib`）；",
          "",
          "旧项目别在 requirements 里固定删掉 setuptools（它同时是构建依赖）；",
          "",
          "判断路径固定：同样的 import 在系统 Python 正常、在干净 venv 报错 = setuptools 缺失，不是代码问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：装 setuptools（最常见、秒级解决）",
          "pip install setuptools",
          "```",
          "",
          "venv 环境就在 venv 里装；系统 Python 用 `sudo pip install setuptools` 或系统包管理装。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 装 setuptools — **必做**",
          "",
          "```bash",
          "pip install setuptools",
          "```",
          "",
          "装完 `import distutils` 即恢复（走 `setuptools/_distutils` 替身）。",
          "",
          "### 2. 顺手升级 pip/setuptools — **必做**",
          "",
          "```bash",
          "python -m ensurepip --upgrade && pip install --upgrade setuptools pip",
          "```",
          "",
          "老 pip（如 PyCharm 内置的 20.3.4）对 3.12+ 兼容差，常一起爆。",
          "",
          "### 3. 系统包管理（仅旧版 Python 有包） — **必做**",
          "",
          "```bash",
          "sudo apt install python3-distutils",
          "```",
          "",
          "注意该包通常只到 Python 3.11；3.12+ 的发行版不一定提供，优先用方案 1。",
          "",
          "### 4. 长期正解（老项目迁移） — **必做**",
          "",
          "旧 setup.py 改为 `pyproject.toml` + setuptools.build_meta（PEP 517），删除代码里对 distutils 的直接 import。急着跑可以先装 setuptools 过渡，但 distutils 已 EOL，不会回来。"
        ],
        "怎么确认修好了": [
          "",
          "import 恢复（实测输出形态）：",
          "",
          "```text",
          "venv 内 pip install setuptools 后 → import distutils 恢复，exit 0（实测）",
          "加载路径变为 venv 内 setuptools/_distutils/__init__.py（实测）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 干净 venv（3.12+） | 只带 pip 25.x，无 setuptools → 报错（实测） |",
          "| 系统 Python | Ubuntu 预装 setuptools 78.1.1，正常 |",
          "| PyCharm 内置 pip | 老 pip 兼容差，顺手升级 |",
          "| apt python3-distutils | 只到 Python 3.11 |",
          "| 长期 | 迁移 pyproject.toml（PEP 517） |",
          ""
        ],
        "症状": [
          "",
          "换到 Python 3.12+（含 3.13/3.14）后，运行旧工具、激活新虚拟环境、或在 PyCharm 导入新解释器时直接报：",
          "",
          "```",
          "ModuleNotFoundError: No module named 'distutils'",
          "```",
          "",
          "特征：老项目/老工具链最常见——`from distutils.core import setup` 的旧 setup.py、依赖 distutils 的旧库、以及默认不带 setuptools 的新建 venv（3.12 起 venv 只带 pip 不再带 setuptools）。",
          ""
        ],
        "原因": [
          "",
          "Python 3.12 起 **distutils 从标准库移除**（此前已废弃多年）：stdlib 里不再有 distutils 模块。同时 3.12 开始新建 venv 默认**只装 pip、不再预装 setuptools**，而 distutils 的替身（shim）恰好由 setuptools 提供——`import distutils` 实际映射到 `setuptools/_distutils`。所以报错本质是「环境中没有 setuptools，也就没有 distutils 替身」。",
          ""
        ],
        "解法": [
          "",
          "按推荐顺序选：",
          "",
          "1. **装 setuptools（最常见、秒级解决）**：`pip install setuptools`。装完 `import distutils` 即恢复（走 `setuptools/_distutils` 替身）。venv 环境就在 venv 里装；系统 Python 用 `sudo pip install setuptools` 或系统包管理装。",
          "2. **顺手升级 pip/setuptools**：`python -m ensurepip --upgrade && pip install --upgrade setuptools pip`——老 pip（如 PyCharm 内置的 20.3.4）对 3.12+ 兼容差，常一起爆。",
          "3. **系统包管理（Debian/Ubuntu，仅旧版 Python 有包）**：`sudo apt install python3-distutils`。注意该包通常只到 Python 3.11；3.12+ 的发行版不一定提供，优先用方案 1。",
          "4. **长期正解（老项目迁移）**：旧 setup.py 改为 `pyproject.toml` + setuptools.build_meta（PEP 517），并删除代码里对 distutils 的直接 import。急着跑可以先装 setuptools 过渡，但 distutils 已 EOL，不会回来。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，Python 3.14.4 / pip 25.1.1）：",
          "  - 系统 Python：Ubuntu 预装 setuptools 78.1.1，`import distutils` 正常，实际加载路径是 `/usr/lib/python3/dist-packages/setuptools/_distutils/__init__.py`（替身生效）；",
          "  - 干净 venv（3.12+ 默认只带 pip 25.1.1、无 setuptools）：`import distutils` → 完整复现 `ModuleNotFoundError: No module named 'distutils'`，退出码 1；",
          "  - 修复：venv 内 `pip install setuptools` 后 `import distutils` 恢复，加载路径变为 venv 内 `setuptools/_distutils/__init__.py`，退出码 0；",
          "  - 结论与文章一致且修正其表述：setuptools 不是「依赖 distutils」，而是**提供 distutils 替身**；`python3-distutils` apt 包仅适用于旧版 Python，3.12+ 优先 `pip install setuptools`。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/python-distutils-removed",
        "agent": "https://trove.xin/agent/problems/python-distutils-removed"
      }
    },
    {
      "id": "python-unicode-decode",
      "title": "Python 读文件报 UnicodeDecodeError（utf-8/gbk codec can't decode byte）——文件编码与默认编码不匹配",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "encoding-format",
      "category_label": "编码与格式",
      "applies_to": [
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/TitorX/Postdown/issues/7",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`open(path).read`、`pandas.read_csv(path)`、`configparser` 等读文件时直接抛异常：Windows 中文环境报 `'gbk' codec can't decode byte 0xad ...`；Linux/mac 报 `'utf-8' codec can't decode byte 0xd6 ...`。",
          "",
          "文件是别人发的/从旧系统拷来的/Excel 另存的。**不是文件坏了，也不是代码逻辑错——是「文件编码」与「读取环境」不匹配。**",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "`open` 不指定 `encoding` 时用**系统默认编码**：",
          "",
          "| 环境 | 默认编码 |",
          "|---|---|",
          "| Windows 中文版 | GBK（cp936）——读 UTF-8 文件报 gbk 错 |",
          "| Linux/mac | UTF-8——读 GBK/旧文件报 utf-8 错 |",
          "",
          "文件实际编码 ≠ 默认编码时，解码到第一个非 ASCII 字节就抛 UnicodeDecodeError。",
          "",
          "`open` 不传 `encoding` 时用「系统默认编码」（locale 决定），而不是「文件实际编码」——Python 无法知道文件是什么编码，只能按默认假设解码。Windows 中文版默认 GBK（cp936）、Linux/mac 默认 UTF-8；文件编码与默认编码不一致时，解码到第一个非 ASCII 字节就抛 UnicodeDecodeError。",
          "",
          "确定性推论：报错里的编码名就是**当前环境的默认编码**（`gbk` codec can't decode = 文件是 UTF-8、环境默认 GBK），不是文件「坏了」；解法是显式指定：读 UTF-8 文件写 `open(f, encoding=\"utf-8\")`（或 `utf-8-sig` 兼容 BOM），读 GBK 旧文件写 `encoding=\"gbk\"`；",
          "",
          "脚本跨机器跑（Windows/Linux）必须显式 encoding，否则同一脚本两台机器行为不同；判断路径：`file <文件>` 看真实编码，再对照报错里的 codec 名。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先看文件真实编码，再显式指定（GBK 例）",
          "file xxx.txt && python3 -c \"print(open('xxx.txt', encoding='gbk').read)\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看文件真实编码 — **必做**",
          "",
          "```bash",
          "file xxx.txt",
          "```",
          "",
          "输出 UTF-8 / ISO-8859（GBK）/ GB18030 等；或 `xxd xxx.txt | head` 看字节。",
          "",
          "### 2. 读时显式指定 encoding（正解） — **必做**",
          "",
          "读 GBK 文件：",
          "",
          "```python",
          "open(path, encoding='gbk')",
          "```",
          "",
          "GB18030 更全：`encoding='gb18030'` 兼容 GBK 且不丢生僻字。读 UTF-8 文件：`open(path, encoding='utf-8')`（在 Windows 上也稳）；pandas：`pd.read_csv(path, encoding='gbk')`。",
          "",
          "### 3. 统一转码（治本，适合批量） — **必做**",
          "",
          "```bash",
          "iconv -f gbk -t utf-8 old.txt > new.txt",
          "```",
          "",
          "或 Excel 里另存为 UTF-8；项目里约定所有文本文件统一 UTF-8。",
          "",
          "### 4. 兜底容忍 — **必做**",
          "",
          "`open(path, errors='replace')` 或 `errors='ignore'` 不抛异常——但坏字节变 `�`，**内容不可逆丢失**，只适合日志/非关键解析。",
          "",
          "### 5. stdout 也乱码时 — **必做**",
          "",
          "print 中文在 GBK locale 下输出乱码，加 `PYTHONIOENCODING=utf-8`（或 `sys.stdout.reconfigure(encoding='utf-8')`）——那是输出编码问题，与读取分开排查。"
        ],
        "怎么确认修好了": [
          "",
          "显式编码读取正常（实测输出形态）：",
          "",
          "```text",
          "encoding='gbk' 读 GBK 文件 → 正常输出「中文测试行」（exit 0，实测）",
          "GBK locale 下 encoding='utf-8' 读 UTF-8 文件 → 正常（exit 0，实测）",
          "```",
          "",
          "复现基线：默认读 GBK 文件 → `'utf-8' codec can't decode byte 0xd6`（exit 1，实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows 中文 | 默认 GBK，读 UTF-8 报 gbk 错（实测复现 B） |",
          "| Linux/mac | 默认 UTF-8，读 GBK 报 utf-8 错（实测复现 A） |",
          "| GB18030 | 兼容 GBK 且不丢生僻字 |",
          "| errors=replace | 不抛异常但坏字节变 �（不可逆） |",
          "| stdout 乱码 | PYTHONIOENCODING=utf-8（输出编码，分开排查） |",
          ""
        ],
        "症状": [
          "",
          "`open(path).read`、`pandas.read_csv(path)`、`configparser` 等读文件时直接抛异常：",
          "",
          "```",
          "UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 2: illegal multibyte sequence",
          "UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0: invalid continuation byte",
          "```",
          "",
          "特征：**Windows 中文环境**（默认 GBK/cp936）读 UTF-8 文件报 `'gbk' codec can't decode`——最常见；Linux/mac（默认 UTF-8）读 GBK/旧文件报 `'utf-8' codec can't decode`；文件是别人发的/从旧系统拷来的/Excel 另存的（GBK 或 GB18030）；`file xxx` 显示 ISO-8859 之类（非 UTF-8）。",
          ""
        ],
        "原因": [
          "",
          "`open` 不指定 `encoding` 时用**系统默认编码**：Windows 中文版是 GBK（cp936），Linux/mac 是 UTF-8。文件实际编码 ≠ 默认编码时，解码到第一个非 ASCII 字节就抛 UnicodeDecodeError。这是「文件编码」与「读取环境」不匹配，不是文件坏了，也不是代码逻辑错。",
          ""
        ],
        "解法": [
          "",
          "1. **先看文件真实编码**：`file xxx.txt`（输出 UTF-8 / ISO-8859（GBK）/ GB18030 等），或 `xxd xxx.txt | head` 看字节。",
          "2. **读时显式指定 encoding（正解）**：",
          "   - 读 GBK 文件：`open(path, encoding='gbk')`（GB18030 更全：`encoding='gb18030'` 兼容 GBK 且不丢生僻字）；",
          "   - 读 UTF-8 文件：`open(path, encoding='utf-8')`（在 Windows 上也稳）；",
          "   - pandas：`pd.read_csv(path, encoding='gbk')`。",
          "3. **统一转码（治本，适合批量）**：`iconv -f gbk -t utf-8 old.txt > new.txt`（或 Excel 里另存为 UTF-8）；项目里约定所有文本文件统一 UTF-8。",
          "4. **兜底容忍**：`open(path, errors='replace')` 或 `errors='ignore'` 不抛异常——但坏字节变 `�`，**内容不可逆丢失**，只适合日志/非关键解析。",
          "5. **stdout 也乱码时**：print 中文在 GBK locale 下输出乱码，加 `PYTHONIOENCODING=utf-8`（或 `sys.stdout.reconfigure(encoding='utf-8')`）——那是输出编码问题，与读取分开排查。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，python 3.14.4；UTF-8 文件与 GBK 文件各一，iconv 生成）：",
          "  - 复现 A（Linux 视角）：默认读 GBK 文件 → `UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0: invalid continuation byte`（exit 1）；",
          "  - 复现 B（Windows 中文视角）：`LC_ALL=zh_CN.GBK`（localedef 生成该 locale）读 UTF-8 文件 → `UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 2: illegal multibyte sequence`（exit 1）——与 Windows 用户报错原文一致；",
          "  - 修复 A：`encoding='gbk'` 读 GBK 文件 → 正常输出「中文测试行」（exit 0）；",
          "  - 修复 B：GBK locale 下 `encoding='utf-8'` 读 UTF-8 文件 → 正常（exit 0；配合 PYTHONIOENCODING=utf-8 stdout 无乱码）；",
          "  - 兜底：`errors='replace'` → 不抛异常（exit 0）但中文变 `�`（不可逆，演示「容忍≠保真」）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/python-unicode-decode",
        "agent": "https://trove.xin/agent/problems/python-unicode-decode"
      }
    },
    {
      "id": "python-venv-move",
      "title": "venv 移动/复制目录后失效（bad interpreter: No such file or directory / venv/bin/activate: No such file or directory）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "python"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/bmaltais/kohya_ss/issues/856",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "项目（连同 venv 目录）挪了位置、拷到新机器、或换用户名路径后，执行 venv 里的命令报 `bad interpreter: No such file or directory`，报错里出现的**旧路径你早就不用了**。最迷惑的是 `venv/bin/python -V` 还能正常输出——看起来环境没坏，但 pip/其它入口脚本全挂。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "venv 里的入口脚本（pip、accelerate、uvicorn 等）第一行 shebang 写的是**创建时的绝对路径**（`#!/old/path/venv/bin/python3`）。目录一挪、换用户、换机器，旧路径不存在，内核直接报 `bad interpreter`（exit 126）。",
          "",
          "而 `venv/bin/python` 本身只是指向系统解释器的软链，跟位置无关，所以还能跑——这就是「python 正常、pip 报错」的真相。kohya_ss#856 是典型：报错里两个路径用户名不同，就是项目从一台机器的用户目录拷到另一用户目录。",
          "",
          "> 实测环境：WSL Ubuntu，python 3.14.4，2026-08-29。",
          "",
          "venv 的入口脚本（pip、uvicorn、加速工具等）是「固定 shebang」：创建时把解释器绝对路径写死在第一行（`#!/old/path/venv/bin/python3`），运行时由内核直接按这个路径启动解释器——目录移动、换用户、换机器后旧路径不存在，内核报 `bad interpreter`（exit 126）。",
          "",
          "而 `venv/bin/python` 只是指向系统解释器的软链，与位置无关，所以「python 还能跑、pip 报错」是这类问题的指纹。",
          "",
          "确定性推论：报错里 shebang 路径与当前路径不一致 = venv 已失效；两条正路——重建 venv（`python3 -m venv .venv && pip install -r requirements.txt`，最干净）或原地修复（`python3 -m venv --upgrade .venv` 重写 shebang）；",
          "",
          "venv 不该进 git（每台机器路径不同），用 requirements/lock 文件重建；",
          "",
          "判断路径：`head -1 .venv/bin/pip` 看 shebang，与 `pwd` 对比即知。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：删掉重建（venv 不保证可移植，别想着修 shebang）",
          "venv/bin/python -m pip freeze > requirements.txt && rm -rf venv && python3 -m venv venv && venv/bin/python -m pip install -r requirements.txt",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 导出依赖 — **必做**",
          "",
          "```bash",
          "venv/bin/python -m pip freeze > requirements.txt",
          "```",
          "",
          "### 2. 删掉重建 — **必做**",
          "",
          "```bash",
          "rm -rf venv",
          "python3 -m venv venv",
          "```",
          "",
          "### 3. 重装依赖 — **必做**",
          "",
          "```bash",
          "venv/bin/python -m pip install -r requirements.txt",
          "```",
          "",
          "### 4. 重跑入口命令确认 — **必做**",
          "",
          "```bash",
          "venv/bin/accelerate --version",
          "```",
          "",
          "不再报 `bad interpreter` 即成功。",
          "",
          "### 5. 治本 — **必做**",
          "",
          "- venv 不进 git，拷项目不连带拷 venv；",
          "- 需要「可移动」环境用 Docker 镜像，或 uv/poetry/conda（按名管理环境，路径无关）；",
          "- `source venv/bin/activate` 报不存在时：先 `ls venv/bin/activate` 确认目录是否真的建出来了（常见于创建步骤失败/被 .gitignore 无视），不存在就重建。",
          "",
          "> 临时兜底：`venv/bin/python -m pip ...` 绕过 shebang 仍可用（实测），但已装的依赖还在旧 site-packages 里，路径一变照样要重装，别当长期方案。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "venv/bin/pip --version",
          "```",
          "",
          "正常输出版本号即成功。实测：移动前 pip 25.1.1 正常；`mv` 后报 `bad interpreter`（exit 126）；删掉重建 → 装 pyyaml 成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 项目挪目录 | 删掉重建，别修 shebang |",
          "| 换用户名/机器 | 旧绝对路径失效，同样重建 |",
          "| 拷项目带 venv | 连带失效；venv 不进 git |",
          "| 需要可移动环境 | Docker / uv / poetry / conda |",
          ""
        ],
        "症状": [
          "",
          "项目（连同 venv 目录）挪了位置、拷到新机器、或换用户名路径后，执行 venv 里的命令报错：",
          "",
          "```",
          "sh: /Users/XXXi/VLADDiffusion/kohya_ss/venv/bin/accelerate: /Users/dflagmei/VLATDiffusion/kohya_ss/venv/bin/python: bad interpreter: No such file or directory",
          "```",
          "",
          "特征：报错里出现的**旧路径**你早就不用了；`source venv/bin/activate` 也可能报 `No such file or directory`；",
          "最迷惑的是 `venv/bin/python -V` 还能正常输出版本号，看起来环境没坏。",
          ""
        ],
        "原因": [
          "",
          "venv 里的入口脚本（pip、accelerate、uvicorn 等）第一行 shebang 写的是**创建时的绝对路径**",
          "（`#!/old/path/venv/bin/python3`）。目录一挪、换用户、换机器，旧路径不存在，内核直接报",
          "`bad interpreter`（退出码 126）。而 `venv/bin/python` 本身只是指向系统解释器的软链，跟位置无关，",
          "所以还能跑——这就是「python 正常、pip 报错」的真相。`activate` 同理：脚本内容是相对引用，",
          "但 venv 目录若被删/没建成功，`source` 就报文件不存在。kohya_ss#856 是典型：报错里两个路径",
          "用户名不同，就是项目从一台机器的用户目录拷到另一用户目录。",
          ""
        ],
        "解法": [
          "",
          "1. **正解：删掉 venv 重建**（venv 不保证可移植，别想着修 shebang）：",
          "   - 有依赖清单先导出：`venv/bin/python -m pip freeze > requirements.txt`；",
          "   - `python3 -m venv venv` 重建，再 `venv/bin/python -m pip install -r requirements.txt`；",
          "   - 重跑入口命令确认。",
          "2. **临时兜底**：`venv/bin/python -m pip ...` 绕过 shebang 仍可用（实测），但已装的依赖",
          "   还在旧 site-packages 里，路径一变照样要重装，别当长期方案。",
          "3. **治本**：venv 不进 git、拷项目不连带拷 venv；需要「可移动」环境用 Docker 镜像、",
          "   或 uv/poetry/conda（按名管理环境，路径无关）。",
          "4. `source venv/bin/activate` 报不存在时：先 `ls venv/bin/activate` 确认目录是否真的建出来了",
          "   （常见于创建步骤失败/被 .gitignore 无视），不存在就重建。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，python 3.14.4）：",
          "  - 创建 venv → `head -1 venv/bin/pip` = `#!/tmp/venv-demo/bin/python3`（绝对路径坐实）；",
          "  - 移动前 `venv/bin/pip --version` 正常（pip 25.1.1）；",
          "  - `mv /tmp/venv-demo /tmp/venv-moved` 模拟挪目录 → `pip: /tmp/venv-demo/bin/python3: bad interpreter: No such file or directory`（exit 126），与 kohya_ss#856 同款；",
          "  - 移动后 `venv/bin/python -V` 仍 exit 0（软链误导点复现）；`venv/bin/python -m pip --version` 也仍可用（兜底成立）；",
          "  - 修复链路：删掉重建 → pip 装 pyyaml 成功（顺带实测：校园网 pypi.org 直连 ReadTimeout 超时 exit 124，换清华镜像成功）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/python-venv-move",
        "agent": "https://trove.xin/agent/problems/python-venv-move"
      }
    },
    {
      "id": "ruflo-1",
      "title": "`ruflo plugins install` 命令不存在（正确技能入口是 npx skills add）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "ruflo"
      ],
      "evidence": "site docs#1;local repro log 2026-08-29",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "你跑 `ruflo migrate status`（或其它提示命令），工具贴心地建议你执行 `ruflo plugins install <plugin>` 修复——但这条命令根本不存在，或语法错误，复制粘贴直接失败。这不是你敲错了，是**工具自己的提示文案指向了错误的子系统和语法**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：插件/技能安装面不是 `plugins install`（issue #2985，2026-08-12），提示文案与实际命令面脱节。",
          "",
          "链路：工具内提示 → 你执行 → 命令面解析 → 无此命令/语法错误。断点在第二步：**用户被引导到不存在的入口**——这是工具的错误报告缺口，不是你的操作问题。",
          "",
          "实测（2026-08-29，v3.38.20）：`npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect` 成功；init 日志明确给出技能安装的正确入口：`Want all 267 plugin skills? npx skills add ruvnet/ruflo --all`。",
          "",
          "推论：遇到「工具提示你跑它建议的命令却跑不通」，以 `ruflo --help` 的实际命令面为准——实测命令面为 init/start/status 等 PRIMARY COMMANDS；拿不准再查官方文档或对应 issue。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：初始化（跳过交互与注册）",
          "npx -y ruflo init --no-signup --no-codex-detect",
          "# 技能安装的正确入口（init 日志同款提示）：",
          "npx skills add ruvnet/ruflo --all",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看真实命令面 — **必做**",
          "",
          "`ruflo --help`：PRIMARY COMMANDS 为 init/start/status 等；没有 `plugins install`。",
          "",
          "### 2. 技能安装用 skills 入口 — **必做**",
          "",
          "`npx skills add ruvnet/ruflo --all`（267 个插件技能；本市场 init 时用 `--no-skills-sh` 跳过了全量拉取，未实测全量，如实标注）。",
          "",
          "### 3. 初始化用 init — **必做**",
          "",
          "`npx -y ruflo init`；交互注册场景加 `--no-signup --no-codex-detect`。",
          "",
          "### 4. 命令拿不准时 — **排查**",
          "",
          "以 `--help` 为准，再查 issue（如 #2985）确认是否已知的提示文案缺陷。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `ruflo --version` 输出版本（实测 `ruflo v3.38.20`）；",
          "- init 正常完成并给出后续提示（技能入口、daemon 提示等）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- 国内网络：init 建议走 npmmirror（`npm_config_registry=https://registry.npmmirror.com`）；",
          "- 常见混淆：`migrate status` 的提示文案 ≠ 可执行命令，以 `--help` 为准；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "`ruflo migrate status` 等提示建议执行 `ruflo plugins install <plugin>`，但该命令不存在或语法错误，打印的修复无法成功。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "错误的子系统 + 错误的语法（issue #2985，2026-08-12）：插件/技能安装面不是 `plugins install`。实测 init 输出显示技能安装正确入口是 `npx skills add ruvnet/ruflo --all`（267 个插件技能）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 技能安装：`npx skills add ruvnet/ruflo --all`（init 输出建议；本市场 2026-08-29 init 时 `--no-skills-sh` 跳过，未实测全量拉取）。",
          "2. 初始化：`npx -y ruflo init`（交互注册场景用 `--no-signup --no-codex-detect` 跳过）。",
          "3. 不确定的命令先 `ruflo --help` 查命令面（实测输出：init/start/status 等 PRIMARY COMMANDS）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 2026-08-29 实测：npmmirror init 成功，`ruflo --version` → `ruflo v3.38.20`；init 日志明确提示 `Want all 267 plugin skills? npx skills add ruvnet/ruflo --all`。",
          "- 素材：issue #2985（2026-08-12）匿名检索摘要。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ruflo-1",
        "agent": "https://trove.xin/agent/problems/ruflo-1"
      }
    },
    {
      "id": "ruflo-2",
      "title": "ruflo 安装被未发布依赖 pin 卡住（@claude-flow/mcp alpha 未发布）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "ruflo"
      ],
      "evidence": "site docs#2;local repro log 2026-08-29",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "执行安装/CI 时，解析依赖的阶段卡住不往前走，报 `@claude-flow/mcp` 相关依赖无法解析——你重试、换网络都没用。这不是网络问题：**依赖 pin 指向了一个还没发布的版本**，解析器找不到，怎么试都不会出现。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：依赖声明里 pin 了一个未发布的版本（issue #3095，open 2026-08-25：`@claude-flow/mcp` alpha.10 未发布），解析期直接失败。",
          "",
          "链路：npm/npx 解析依赖 → 拉取 registry 元数据 → 按 pin 版本查找 → 版本不存在 → 解析失败。断点在第三步：**发布节奏问题，客户端无解**。",
          "",
          "对照实测（2026-08-29）：本环境 `npx -y ruflo init`（v3.38.20 + npmmirror）没有复现——此时版本已满足（如实标注未复现）；说明这是一个时间窗问题：上游发布后自愈。",
          "",
          "推论：pin 类解析失败先验证「该版本是否真实存在」（`npm view <包> versions`），再选路：等发布、锁可用版本区间、或换源重试（npmmirror 同步有延迟时回官方源）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先确认 pin 的版本是否存在（不存在就该等上游，不是重试）",
          "npm view @claude-flow/mcp versions",
          "# 已发布版本就位后重试安装：",
          "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 验证版本是否存在 — **必做**",
          "",
          "`npm view @claude-flow/mcp versions`：若 pinned 版本不在列表 = 未发布，重试无意义。",
          "",
          "### 2. 等发布或锁可用版本 — **按场景**",
          "",
          "上游发布后重试；或在你自己的依赖里锁定已发布的可用版本区间。",
          "",
          "### 3. 换源再试 — **排查**",
          "",
          "npmmirror 同步有延迟时回官方源；反向同理（官方慢时走 npmmirror）。",
          "",
          "### 4. 反馈上游 — **排查**",
          "",
          "确认是未发布 pin 后，在对应 issue 里跟进（#3095），不要在本地下功夫。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- 依赖解析阶段正常推进直至安装完成；",
          "- `ruflo --version` 正常输出；init 流程可跑通（2026-08-29 实测同款路径）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- CI 流水线：解析失败会让整条流水线红；先判定 pin 问题再改流水线；",
          "- 时间窗：上游发布后自愈——遇到时先查 issue 时间线；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "安装/CI 卡在依赖解析，`@claude-flow/mcp` 相关依赖无法解析。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "依赖 pin 指向未发布版本（issue #3095，open 2026-08-25：@claude-flow/mcp alpha.10 未发布），解析期失败。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 等上游发布后重试；或安装时锁定已发布的可用版本区间。",
          "2. 换 npm 源重试（npmmirror 同步有延迟时回官方源）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：issue #3095（open，2026-08-25）匿名检索摘要；本环境 2026-08-29 init（v3.38.20 + npmmirror）未复现（版本已满足），如实标注未复现。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ruflo-2",
        "agent": "https://trove.xin/agent/problems/ruflo-2"
      }
    },
    {
      "id": "ruflo-3",
      "title": "Windows 上 `hive-mind spawn --claude` 检测不到 claude CLI（PATH 检测失败）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "coexist-compat",
      "category_label": "组合与兼容",
      "applies_to": [
        "ruflo"
      ],
      "evidence": "site docs#3;local repro log 2026-08-30",
      "source": "本站实测沉淀（2026-08-29）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "Windows 上跑 `hive-mind spawn --claude`，ruflo 说找不到 `claude` 命令——但你的终端里 `claude` 明明能跑（或者你刚装过）。这是典型的**跨工具 PATH 检测失败**：另一个工具的探测逻辑没认到 Windows 的 PATH 语义。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：PATH 检测失败（issue #3071，closed 2026-08-20）——Windows 的 PATH 语义/安装位置与 Unix 差异大，探测器没识别到 claude CLI。",
          "",
          "链路：ruflo spawn → 探测 claude 可执行文件 → Windows PATH 查找（`.cmd`/`.exe` shim、npm 全局 bin 目录）→ 未命中 → 报「找不到命令」。断点：**安装位置不在 PATH，或探测逻辑不认 shim**。",
          "",
          "实测对照：本环境（WSL Linux）swarm 本地路径正常；**Windows PATH 路径未实测**（如实标注）。",
          "",
          "推论：跨工具集成故障先做两步手动验证——`claude --version` 能不能跑（`where claude` 在哪），能跑就是探测层问题：把目录加进 PATH 或改用完整路径。",
          ""
        ],
        "快速修复": [
          "",
          "```text",
          "Windows 排查两步（机制同 Unix）:",
          "1) claude --version        验证 CLI 本身可用；",
          "2) where claude            看安装位置是否在 PATH；不在则把该目录加入 PATH（或系统 PATH），重开终端；",
          "仍不识别时：用完整路径调用，并检查 npm 全局 bin 目录是否在 PATH（claude.cmd 类 shim 所在目录）。",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 手动验证 claude CLI — **必做**",
          "",
          "`claude --version` + `where claude`：命令本身可用 = 问题在探测层，不在安装。",
          "",
          "### 2. 把目录补进 PATH — **必做**",
          "",
          "在系统「环境变量」里把 claude 所在目录（含 `claude.cmd` shim 的 npm bin 目录）加进 PATH，重开终端再试。",
          "",
          "### 3. 完整路径兜底 — **按场景**",
          "",
          "探测类工具支持自定义路径时，直接给 claude 的完整可执行路径。",
          "",
          "### 4. 仍失败则跟进 issue — **排查**",
          "",
          "确认 PATH 正确仍不识别，跟进 ruvnet/ruflo #3071（closed）的处理结论或开新 issue。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- `hive-mind spawn --claude` 不再报「找不到 claude」；",
          "- 相关子进程正常拉起（日志里能看到 claude 会话）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- WSL / Linux：swarm 本地路径实测正常（2026-08-30）；",
          "- Windows：PATH 语义与 shim 识别是高频坑（#3071）；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "Windows 环境执行 spawn --claude 报找不到 claude 命令。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "PATH 检测失败（issue #3071，closed 2026-08-20）：Windows PATH 语义/安装位置差异，未识别到 claude CLI。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 把 claude 可执行文件所在目录加入 PATH 后用完整路径调用。",
          "2. 确认 claude CLI 已安装（`claude --version`）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 素材：issue #3071（closed，2026-08-20）匿名检索摘要；本环境 WSL Linux，Windows PATH 路径**未实测**（如实标注）。",
          ""
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ruflo-3",
        "agent": "https://trove.xin/agent/problems/ruflo-3"
      }
    },
    {
      "id": "ruflo-4",
      "title": "首次 npx ruflo init 很慢、npx 缓存约 2GB（磁盘敏感环境）",
      "domain": "multi-agent",
      "domain_label": "多智能体编排",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "ruflo"
      ],
      "evidence": "site docs#4;local repro log 2026-08-29",
      "source": "本站实测沉淀（2026-08-27）",
      "recorded": "2026-09-19",
      "sections": {
        "你会遇到什么": [
          "",
          "第一次 `npx ruflo init` 要等约 5 分钟，而且磁盘占用突然变大——一查 `~/.npm/_npx` 涨到 1.2GB+，加上全局缓存合计约 2GB 级。这不是 ruflo 本体大，而是 **npx -y 的缓存机制 + 原生 prebuild 下载**叠出来的体积。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先给结论：慢在「首次下载」，大在「npx 缓存双份」。",
          "",
          "链路：`npx -y ruflo ...` → 从 registry 拉包 → 包内原生 prebuild 下载 → 全部落进 `~/.npm/_npx` 缓存。断点/放大点：npx -y 每次运行都把包解到缓存（隔离 HOME 与全局各一份：2026-08-29 实测 1.2GB + 775MB），原生预构建下载占大头（2026-08-27 基线约 5 分钟、约 1.6GB）。",
          "",
          "实测（2026-08-29，）：npmmirror init 成功（v3.38.20），全程秒级下载完成——慢的确定性部分被换源消掉了；缓存体积仍在。",
          "",
          "推论：慢 → 换 npmmirror（确定性收益）；大 → 验证完清缓存（`rm -rf ~/.npm/_npx`，不影响已装项目）。磁盘敏感环境（CI/容器/小盘 VPS）两招一起用。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：换 npmmirror（秒级下载）+ 跳过交互/注册/技能全量拉取",
          "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect",
          "# 验证完清缓存（不影响已装项目）：",
          "rm -rf ~/.npm/_npx   # 或 npm cache clean --force",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 换源安装 — **必做**",
          "",
          "`npm_config_registry=https://registry.npmmirror.com` 前缀重跑 init；实测从约 5 分钟降到秒级。",
          "",
          "### 2. 减下载面 — **必做**",
          "",
          "`--no-signup --no-skills-sh --no-codex-detect` 跳过交互、注册与技能全量拉取（267 个技能按需再装）。",
          "",
          "### 3. 清理缓存 — **按场景**",
          "",
          "磁盘敏感：`rm -rf ~/.npm/_npx` 或 `npm cache clean --force`；只清缓存，不动已装项目。",
          "",
          "### 4. 核对写面 — **排查**",
          "",
          "init 会写工作区 `.claude/`、`.claude-flow/`、`.swarm/` 等 + HOME `~/.claude/CLAUDE.md`；`--help` 会拉起后台 daemon，测完 `ruflo daemon stop`。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "- init 快速完成（换源后秒级下载）；",
          "- `ruflo --version` 输出版本；",
          "- `du -sh ~/.npm/_npx` 在你接受的范围（清缓存后回落）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "- CI / 容器：缓存体积直接影响镜像层大小，建议装机即清或挂缓存卷；",
          "- 小盘 VPS：2GB 级占用要预留；",
          "",
          ""
        ],
        "症状": [
          "",
          "",
          "`npx ruflo init` 首次安装约 5 分钟，磁盘占用明显变大。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "原生 prebuild 下载 + npx 缓存双份：2026-08-29 实测隔离 HOME 下 `~/.npm/_npx` 1.2GB + 全局 `~/.npm/_npx` 775MB（合计约 2GB 级），npx -y 每次运行都落缓存。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 国内加速：`npm_config_registry=https://registry.npmmirror.com npx -y ruflo ...`（实测 init 全程秒级下载完成）。",
          "2. 磁盘敏感：安装验证完清理 npx 缓存（`rm -rf ~/.npm/_npx` 或 `npm cache clean --force`），不影响已装项目。",
          "3. init 用 `--no-signup --no-skills-sh --no-codex-detect` 跳过交互、注册与技能全量拉取，减少下载面。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 2026-08-29 实测：npmmirror init 成功（v3.38.20）；写面 = 工作区 `.claude/`（agents/commands/helpers/settings.json/skills）+ `.claude-flow/`（CAPABILITIES.md/config.yaml/data/hooks/learning/logs）+ `.agents/skills/ruflo/SKILL.md` + `.swarm/` + `.mcp.json` + `CLAUDE.md` + `ruvector.db`（1.5MB）+ HOME `~/.claude/CLAUDE.md`；`--help` 会拉起后台 daemon，测完 `ruflo daemon stop` 停。",
          "- 2026-08-27 基线：v3.38.20（npmmirror），prebuild 下载约 5 分钟、缓存 1.6GB。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ruflo-4",
        "agent": "https://trove.xin/agent/problems/ruflo-4"
      }
    },
    {
      "id": "script-permission-denied",
      "title": "bash: ./script.sh: Permission denied——脚本缺执行权限",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "bash"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/18960689/ubuntu-says-bash-program-permission-denied",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "从 Windows 拷过来、或压缩包解压出来的脚本，执行 `./setup.sh` 报 `Permission denied`，但 `bash setup.sh` 却能跑。你以为是文件坏了或系统拒绝执行——其实只是**脚本少了执行位（x）**，Linux 的权限模型和 Windows 不一样。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "Linux 用「执行位」决定一个文件能否被直接运行：",
          "",
          "- `./script.sh` 走「直接执行」路径，内核要求文件有 `x` 位；",
          "- `bash script.sh` 是把文件当参数交给 bash 读取，不需要 `x` 位——所以同样一个文件，两种调用方式结果不同。",
          "",
          "新创建的文件默认是 `644`（无执行位）；从 Windows 复制（FAT/exFAT/网络盘）或 zip 解压经常丢执行位；git 仓库 `core.fileMode` 配置不当也可能让 x 位丢失。",
          "",
          "> 实测环境：WSL Ubuntu，2026-08-29。",
          "",
          "Linux 执行一个脚本有两条路径：`./script.sh` 走「直接执行」——内核检查文件有 `x` 位后，读 shebang 找解释器执行；`bash script.sh` 走「解释器读文件」——bash 只是把文件当参数读取，**不检查执行位**。所以同一个文件两种调用结果不同。",
          "",
          "执行位是文件元数据（权限位），新建文件默认 644（无 x）；从 Windows 复制（FAT/exFAT/网络盘不支持 POSIX 权限）或 zip 解压（zip 不存执行位）经常丢；git 检出丢 x 是 `core.fileMode` 配置不当。",
          "",
          "确定性推论：`Permission denied` + 文件存在 = 缺 x 位，`chmod +x` 即修；WSL 挂载盘（/mnt/c）chmod 无效（drvfs 不支持）→ 复制进 WSL 文件系统再 chmod；zip 解压后统一 `chmod +x`；",
          "",
          "git 里 x 位是版本化属性，`git update-index --chmod=+x <file>` 或修 core.fileMode；",
          "",
          "判断路径：`ls -l` 看权限位，第一列无 x = 坐实。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：补执行位后直接运行",
          "chmod +x setup.sh && ./setup.sh",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是权限问题 — **必做**",
          "",
          "```bash",
          "ls -l setup.sh",
          "```",
          "",
          "权限位是 `-rw-r--r--`（没有 x）→ 就是这个问题（实测：`chmod 644` 后 `./nox.sh` 报 Permission denied，exit=126）。",
          "",
          "### 2. 补执行位 — **必做**",
          "",
          "```bash",
          "chmod +x setup.sh",
          "```",
          "",
          "### 3. 再运行 — **必做**",
          "",
          "```bash",
          "./setup.sh",
          "```",
          "",
          "实测：`chmod +x` 后正常输出，exit=0。",
          "",
          "### 4. 批量/预防 — **必做**",
          "",
          "```bash",
          "chmod +x site docs",
          "```",
          "",
          "- git 提交前确认 `git ls-files -s` 显示 `100755`（可执行）；",
          "- 解压 zip 后按需补 x；",
          "- Windows 挂载盘场景按需处理 `core.fileMode`（保持 true 通常更稳）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "./setup.sh",
          "```",
          "",
          "不再报 Permission denied、能正常执行即成功（实测：`bash nox.sh` 也能跑，exit=0——不想改权限时显式用解释器调用即可）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| WSL Ubuntu | 与原生 Linux 一致，补 `chmod +x` |",
          "| Windows 拷贝的文件 | FAT/exFAT/网络盘不带执行位，拷入后必查必补 |",
          "| zip 解压 | unzip 后常丢 x 位，按需补 |",
          "| git 仓库 | `core.fileMode=false` 会让 x 位丢失不同步，保持 true |",
          "| 单次救急 | `bash script.sh` / `python3 app.py` 显式解释器调用，无需 x 位 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ ./setup.sh",
          "bash: ./setup.sh: Permission denied",
          "```",
          "",
          "- `ls -l` 显示脚本权限是 `-rw-r--r--`（没有 x）。",
          "- 明明文件内容没问题、`bash setup.sh` 能跑，`./setup.sh` 却报 Permission denied。",
          "- 常见场景：从 Windows 拷贝/压缩包解压/新创建的文件，执行位丢失。",
          ""
        ],
        "原因": [
          "",
          "1. Linux 用「执行位」决定能否直接运行文件；新文件默认 `644`（无执行位）。",
          "2. `./script.sh` 走「直接执行」路径，需要 x 位；`bash script.sh` 是「把文件当参数读」，不需要 x 位。",
          "3. 从 Windows 复制（FAT/exFAT/网络盘）或 zip 解压常丢执行位；git 仓库里 `core.fileMode` 配置不当也可能让 x 位丢失。",
          ""
        ],
        "解法": [
          "",
          "1. **补执行位**：",
          "   ```",
          "   chmod +x setup.sh",
          "   ./setup.sh",
          "   ```",
          "2. **不想改权限**：显式用解释器 `bash setup.sh` / `python3 app.py`（对单次运行足够）。",
          "3. **批量**：`chmod +x 站内文档。",
          "4. 预防：git 提交前确认 `git ls-files -s` 里是 `100755`（可执行）；解压 zip 用 `unzip` 后按需补 x；`core.fileMode` 保持 true（Windows 挂载盘场景按需处理）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机（WSL Ubuntu，2026-08-29）：",
          "- 实测 1：`chmod 644` + `./nox.sh` → `Permission denied`，exit=126——与用户报错一致。",
          "- 实测 2：`bash nox.sh` → 正常输出，exit=0（解释器调用不需要 x 位）。",
          "- 实测 3：`chmod +x` + `./nox.sh` → 正常，exit=0。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/script-permission-denied",
        "agent": "https://trove.xin/agent/problems/script-permission-denied"
      }
    },
    {
      "id": "site-open-fail-others-ok",
      "title": "只有个别网站打不开（其他网站都正常）——从 curl 超时到分层定位",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "curl"
      ],
      "evidence": "Evidence details kept locally (2026-09-02)",
      "source": "https://www.cnblogs.com/pprof/p/22529721",
      "recorded": "2026-09-02",
      "sections": {
        "你会遇到什么": [
          "",
          "同一台电脑上，`https://www.google.com/`（或百度、GitHub）正常打开，但某一个网站就是打不开：浏览器转圈到最后报「连接超时」或「无法访问此网站」。很多人第一反应是「这个网站挂了」——但**只有个别域名失败，恰恰说明你的网络到大多数站点是通的**，故障通常发生在你到那个站点的某一层：DNS 解析、TCP 连接、TLS 握手、HTTP 响应，或本地代理规则。",
          "",
          "> 本文路径：**快速修复**（30 秒定位命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "浏览器打开一个 HTTPS 网站，至少经过四层：**DNS 解析**（域名 → IP）→ **TCP 连接**（三次握手）→ **TLS 握手**（证书/密钥协商）→ **HTTP 请求**（拿到内容）。任何一层断，浏览器都只给你一个笼统的「打不开」。而失败原因五花八门：",
          "",
          "- **DNS 层**：你的 DNS 服务器对某个域名解析超时/返回坏结果（缓存污染、上游故障、过滤规则）；",
          "- **TCP 层**：域名解析出的 IP 不可达（该 IP 被限、路由黑洞、CDN 节点故障），或端口被拦；",
          "- **TLS/HTTP 层**：证书链问题、SNI 被拦、服务端拒绝——较少见但存在；",
          "- **代理层**：系统代理/Clash 规则把该域名分流到不可达的节点。",
          "",
          "关键认知：**「打不开」只是一个症状，不能直接等于「网站挂了」或「被墙了」**。先用命令行把故障定位到某一层，再决定怎么修——换 DNS、换代理节点、还是换网络路径，方向完全不同。",
          "",
          "「个别域名失败」的排障价值在于它天然做了对照：你的公网出口是通的（其他 HTTPS 站点 200），所以嫌疑被压缩到「你 → 该域名」这条链路上。curl 的 `-v` 输出会明确停在哪一步：`Trying <ip>...` 之后卡住 = TCP 层；`Connected to <ip>` 之后卡住 = TLS 层；能拿到响应头 = 服务层正常。",
          "",
          "`nslookup <域名> <dns服务器>` 可以绕过系统 DNS 单独问某个服务器，`curl --resolve` 可以绕过 DNS 直接指定 IP——这两个命令把「解析」和「连接」拆开，就是分层定位的全部秘密。",
          "",
          "素材案例（2026-08-17 公开排障记录）中，故障机默认 DNS 无法及时解析目标域名，改用 `1.1.1.1`/`8.8.8.8` 后正常获得 CDN 地址——DNS 层故障的典型结局。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：30 秒定位「个别网站打不开」卡在哪一层",
          "# ① 别的站点通不通（对照基线；不通 = 全站问题，先查网关/代理）",
          "curl -sI -m 8 https://www.baidu.com -o /dev/null -w \"baseline http=%{http_code}\\n\"",
          "",
          "# ② DNS 层：目标域名能不能解析出 IP（无输出 = 解析失败 → DNS 层）",
          "getent ahosts 目标域名 | head -1",
          "",
          "# ③ TCP 层：直连看卡在哪（-v 输出停在 Trying = TCP 层；停在 Connected = TLS 层）",
          "curl -v -I -m 10 https://目标域名 2>&1 | grep -E \"Trying|Connected|HTTP/|error|timed\"",
          "",
          "# ④ 绕过 DNS 直连解析出的 IP（②有 IP 才能做）：",
          "#    连上 = DNS 层坏（换 DNS）；依然超时 = 网络路径层坏（换代理/换网络）",
          "curl -sI -m 10 --resolve 目标域名:443:解析出的IP https://目标域名 -o /dev/null -w \"bypass http=%{http_code}\\n\"",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认范围：只有这一个域名失败 — **必做**",
          "",
          "```bash",
          "curl -sI -m 8 https://www.baidu.com -o /dev/null -w \"baseline http=%{http_code}\\n\"",
          "curl -sI -m 8 https://目标域名 -o /dev/null -w \"target  http=%{http_code}\\n\"",
          "```",
          "",
          "基线 200、目标超时/失败 = 进入本文。**两个都失败** = 先查网络本身（网关、代理、DNS 全挂），不适用本文。",
          "",
          "### 2. DNS 层：域名有没有解析结果 — **必做**",
          "",
          "```bash",
          "getent ahosts 目标域名 | head -1",
          "# 期望输出: <IP> STREAM 目标域名",
          "# 无输出（exit 非 0）= 解析失败 -> DNS 层问题，跳到第 5 步换 DNS",
          "```",
          "",
          "有 IP 但打不开 ≠ DNS 没问题——解析出的 IP 可能是坏记录（被污染/过期 CDN 节点），所以要继续第 3 步验证「这个 IP 到底通不通」。",
          "",
          "### 3. TCP/网络路径层：直连这个 IP 通不通 — **必做**",
          "",
          "```bash",
          "curl -v -I -m 10 https://目标域名 2>&1 | tee /tmp/curl-v.txt",
          "# Trying <ip>... 之后长时间无进展 = TCP 层失败（网络路径/该 IP 不可达）",
          "# Connected to <ip> 之后卡住 = TLS 层（少见，看证书与 SNI）",
          "```",
          "",
          "### 4. 绕过 DNS 直连：把 DNS 层和连接层彻底拆开 — **必做**",
          "",
          "```bash",
          "IP=$(getent ahosts 目标域名 | head -1 | awk '{print $1}')",
          "curl -sI -m 10 --resolve 目标域名:443:$IP https://目标域名 -o /dev/null -w \"bypass http=%{http_code}\\n\"",
          "```",
          "",
          "- 绕过后能连上（http=200/301/403 都算服务可达）→ **DNS 层坏**：解析链路给的 IP 不对/不稳定。修法见第 5 步。",
          "- 绕过后依然超时 → **网络路径层坏**：解析本身正常，是你到该 IP 的路不通（被限/黑洞/CDN 节点故障）。换 DNS 没用，见第 6 步。",
          "",
          "### 5. DNS 层坏：换公共 DNS 验证 — 对症",
          "",
          "```bash",
          "# 有 dig 的环境直接对比；没有就换系统 DNS 后重试第 1 步",
          "dig @223.5.5.5 目标域名 +short    # 阿里公共 DNS",
          "dig @1.1.1.1  目标域名 +short     # Cloudflare",
          "dig @8.8.8.8  目标域名 +short     # Google（国内部分网络 UDP 53 不可达，超时不算结论）",
          "```",
          "",
          "换 DNS 的具体落法：Windows 在网络适配器属性里改 DNS；路由器后台改上游 DNS（治本，全家设备生效）；WSL 内改法见相关 canonical「WSL 域名解析失败」。改完立刻重测第 1 步，能开 = 实锤 DNS 层。",
          "",
          "### 6. 网络路径层坏：换路径而不是换 DNS — 对症",
          "",
          "- 系统代理/Clash 类工具：换一个节点再试（规则把该域名分到了不可达节点是高频原因）；",
          "- 有代理就开代理直连测试：`curl -x http://127.0.0.1:7890 -sI -m 10 https://目标域名`（端口按你的代理实际端口）；",
          "- 手机开热点对比：换一条网络路径，能开 = 你原网络到该 IP 的路有问题；",
          "- 上述都无效且确认是访问限制：走合规加速/镜像，见相关 canonical 的 GitHub/镜像条目。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "curl -sI -m 10 https://目标域名 -o /dev/null -w \"http=%{http_code} dns=%{time_namelookup}s connect=%{time_connect}s total=%{time_total}s\\n\"",
          "# 返回 200/301/403 且 total 不再超时 = 修好",
          "```",
          "",
          "浏览器里硬刷新（Ctrl+F5）目标站点能打开 = 用户侧确认。注意 CDN/浏览器缓存：修好后第一次仍失败，等 1-2 分钟或换隐身窗口再试。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows 本机 | 网络适配器属性改 DNS；`ipconfig /flushdns` 清缓存 |",
          "| WSL / Linux | 改 systemd-resolved 上游或 resolv.conf（见 wsl-dns-resolution-failure） |",
          "| 路由器 | 上游 DNS 改公共 DNS，全家生效（治本） |",
          "| Clash/V2Ray 等代理 | 先换节点再下结论——分流规则把域名指向坏节点极常见 |",
          "| 公司/校园网 | 内网域名问内网 DNS；外网个别失败多为出口限制 |",
          "| Docker 容器内 | 容器自带 resolv.conf，见 docker-container-dns |",
          ""
        ],
        "症状": [
          "",
          "「个别网站打不开」的典型表现：",
          "",
          "```",
          "浏览器: 连接超时 / 无法访问此网站 / ERR_CONNECTION_TIMED_OUT",
          "curl:   curl: (28) Connection timed out after 10000 milliseconds",
          "ping:   请求超时（ping 不通不代表网站挂——很多站点禁 ICMP）",
          "```",
          "",
          "特征：**同一网络下其他 HTTPS 站点正常**；失败域名稳定复现（换浏览器也一样）；重启路由/清 DNS 缓存可能短暂恢复又复发（DNS 缓存被刷新的假象）。",
          ""
        ],
        "原因": [
          "",
          "「打不开」是用户侧唯一能看到的症状，它可能来自链路中任何一层：DNS 解析失败或返回坏 IP（缓存污染/上游故障/过滤规则）、TCP 连接超时（IP 不可达、路由黑洞、端口拦截、CDN 节点故障）、TLS 握手失败（证书/SNI，少见）、HTTP 层被拒（服务端策略）。代理工具还会引入自己的断点：分流规则把域名指向不可达节点。**不同层的原因对应完全不同的修法**——这就是必须先分层、再动手的原因。另一个常见误区是用 ping 判断网站状态：ICMP 和 HTTPS 是两条完全不同的路径，ping 不通而浏览器能开、或 ping 通而浏览器超时，都属正常。",
          ""
        ],
        "解法": [
          "",
          "1. **定范围**：基线站点 200、目标站点失败 → 进入「个别域名」排障（都失败先查网络本身）。",
          "2. **查 DNS**：`getent ahosts 目标域名`——无结果 = DNS 层；有结果 ≠ 排除 DNS，继续下一步。",
          "3. **直连看层**：`curl -v -I -m 10 https://目标域名`，输出停在 `Trying` = TCP 层，停在 `Connected` = TLS 层。",
          "4. **绕过 DNS 拆层**：`curl --resolve 目标域名:443:<IP> https://目标域名`——能连 = DNS 层坏；仍超时 = 网络路径层坏。",
          "5. **DNS 层坏**：换公共 DNS（223.5.5.5 / 1.1.1.1），Windows 改适配器、路由器改上游、WSL 改 resolved.conf。",
          "6. **网络路径层坏**：换代理节点、开代理直连测试、手机热点换路径；确认访问限制后走合规镜像/加速。",
          "7. **验证**：curl 响应码 + 浏览器硬刷新；CDN 场景等 1-2 分钟再判。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-09-02，WSL Ubuntu 26.04）：实测复现日志（2026-09-02）",
          "  - 基线：同网络 baidu http=200 → 公网出口正常，符合「个别域名失败」现场；",
          "  - DNS 层：`getent ahosts www.flextv.cc` 正常返回 CNAME（queniuaa.com）与多个 A 记录 → 解析正常，DNS 层排除；",
          "  - TCP 层：`curl -I` 直连 10s 超时（code=28，输出停在 Trying）→ 卡在 TCP 连接；",
          "  - 拆层：`curl --resolve www.flextv.cc:443:<解析IP>` 绕过 DNS 直连依然 10s 超时 → 实锤故障在 TCP/网络路径层，不在 DNS 层（换 DNS 对该案例无效，正确方向是换网络路径）；",
          "  - 坏 IP 对照：指向测试网段 203.0.113.1 同样连不上 → 说明「连不上」这一表现无法区分 DNS 坏还是路径坏，必须用 --resolve 拆层。",
          "- 素材案例（公开排障记录 22529721，2026-08-17）：默认 DNS 无法解析目标域名 → 改用 1.1.1.1/8.8.8.8 后正常获得 CDN 地址 → DNS 层故障的完整证据链（nslookup + --resolve）。",
          "- 未实测（如实标注）：TLS/HTTP 层故障现场（本机未遇）、路由器后台修改（无权限环境）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **前置（先排除）**：",
          "  - [代理环境变量不生效/部分工具不走代理](proxy-env-var.html)（先确认代理变量与 NO_PROXY 语义，再分层）",
          "  - [WSL 域名解析失败](wsl-dns-resolution-failure.html)（WSL 内全域名解析失败走这篇；个别域名失败先看本文）",
          "",
          "- **同域（网络/DNS 家族，环境不同）**：",
          "  - [Docker 容器内域名解析失败](docker-container-dns.html)（容器内个别/全部域名解析失败）",
          "  - [GitHub 直连慢/失败](github-direct-connect-fail.html)（同分层思路：curl 看网络层 → 再加速）",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [curl 大文件中断无法续传](curl-resume-break.html)（同为 curl 场景，但症状在传输阶段）",
          "  - [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（镜像仓库域名连不上时的对症路径）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/site-open-fail-others-ok",
        "agent": "https://trove.xin/agent/problems/site-open-fail-others-ok"
      }
    },
    {
      "id": "skillspector-1",
      "title": "pip install skillspector 找不到包（No matching distribution）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#1;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`pip install skillspector==2.10.0` 直接报 `Could not find a version that satisfies the requirement ... (from versions: none)` 和 `No matching distribution found`。",
          "",
          "你以为镜像源坏了，换清华、换官方都一个样——因为**这个包根本不在 PyPI 上**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "skillspector 的发行渠道是 **GitHub Release**（wheel + sdist 资产），不是 PyPI。按 PyPI 方式安装必然失败；这是国内教程最常见的坑（部分教程写 `pip install git+...` 也是绕行做法）。",
          "",
          "| 安装方式 | 结果 |",
          "|---|---|",
          "| `pip install skillspector`（PyPI） | ❌ 必然失败（PyPI 无此包，三路复核实测） |",
          "| 下载 GitHub Release wheel 后 pip 装 | ✅ 实测成功 |",
          "| `pip install git+https://github.com/NVIDIA/SkillSpector.git` | ✅ 可行（源码方式，国内直连不稳） |",
          "",
          "`pip install <名字>` 的背后是一次**渠道寻址**：pip 按名字去你配置的 index（默认 PyPI）查询，PyPI 上不存在这个名字就返回“找不到版本”。关键是渠道不是全局的——同一个软件可以选择只发布在 GitHub Release 上，而 pip 默认不认识 GitHub Release 这个渠道。",
          "",
          "所以“换镜像源没用”是必然的：清华、官方、阿里都是 PyPI 的镜像，镜像的是同一个渠道，渠道里没有的东西镜像里也不会有。正确的做法是**换渠道而不是换镜像**：从 GitHub Release 下载 wheel 文件，再 `pip install <文件>`——这时 pip 不再按名字寻址，而是直接安装本地文件，依赖仍从 PyPI 拉（所以配国内镜像装依赖）。",
          "",
          "这个机制还解释了三个现象：`pip install git+...` 能行（pip 支持直接从 git 渠道装源码）；wheel 文件名不能改（pip 从文件名解析包名/版本/平台，格式不对直接拒绝）；`(from versions: none)` 里的 none 意味着“该渠道没有任何版本”，是渠道问题的特征信号。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：下载 release wheel（走 gh-proxy 加速）后 pip 安装（清华镜像装依赖）",
          "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl && pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl",
          "```",
          "",
          "注意：wheel 文件名必须保持原样（pip 校验文件名格式，改短会报 `Invalid wheel filename`）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认渠道 — **必做**",
          "",
          "`pip index versions skillspector` 报 No matching distribution = PyPI 无此包，别再试 PyPI 了。",
          "",
          "### 2. 下载 release wheel — **必做**",
          "",
          "```bash",
          "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl",
          "```",
          "",
          "gh-proxy 国内加速；直连 GitHub 不稳时必用。",
          "",
          "### 3. pip 安装 wheel — **必做**",
          "",
          "```bash",
          "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl",
          "```",
          "",
          "依赖走清华镜像，wheel 本体来自 GitHub Release。",
          "",
          "### 4. 验证命令面 — **必做**",
          "",
          "```bash",
          "skillspector --version",
          "```",
          "",
          "预期输出 `SkillSpector v2.10.0`（实测）。"
        ],
        "怎么确认修好了": [
          "",
          "版本命令输出正确（实测输出）：",
          "",
          "```text",
          "skillspector --version",
          "→ SkillSpector v2.10.0",
          "```",
          "",
          "安装日志以 install exit=0 结束（实测闭环：wheel 下载 468K → 依赖全部安装成功）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 国内直连 GitHub | 走 gh-proxy 下载 wheel（实测） |",
          "| 没有 gh-proxy | 可换可达镜像，或 `pip install git+...` 源码方式 |",
          "| 文件名被改短 | 报 `Invalid wheel filename`，必须保持原样 |",
          "| 依赖安装慢 | `-i https://pypi.tuna.tsinghua.edu.cn/simple`（实测可用） |",
          "| Windows / macOS | 同样方式，wheel 是 py3-none-any 通用 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`pip install skillspector==2.10.0` 报：",
          "",
          "```",
          "ERROR: Could not find a version that satisfies the requirement skillspector==2.10.0 (from versions: none)",
          "ERROR: No matching distribution found for skillspector==2.10.0",
          "```",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "**skillspector 不在 PyPI 上**——发行渠道 = GitHub Release（wheel + sdist 资产）。按 PyPI 方式安装必然失败；这是国内教程最常见的坑（部分教程写 `pip install git+...` 也是绕行做法）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 正确姿势（本市场实测）：下载 release wheel 后 pip 安装：",
          "   ```",
          "   curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl",
          "   pip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl",
          "   ```",
          "2. 注意 wheel 文件名必须保持原样（pip 校验文件名格式，改短会报 `Invalid wheel filename`）。",
          "3. 也可 `pip install git+https://github.com/NVIDIA/SkillSpector.git`（源码方式，国内直连不稳）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测 2026-08-28（实测记录（本地留存））：PyPI JSON API → `{\"message\": \"Not Found\"}`；清华镜像 simple → 404；`pip index versions` → No matching distribution。三路复核确认 PyPI 无此包。",
          "- 闭环 v3：wheel 下载 468K → pip 安装全部依赖成功 → `SkillSpector v2.10.0` → install exit=0。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-1",
        "agent": "https://trove.xin/agent/problems/skillspector-1"
      }
    },
    {
      "id": "skillspector-2",
      "title": "Failed building wheel for yara-python（构建依赖缺失）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "pip 安装 skillspector 时，依赖 yara-python 编译失败：`Failed building wheel for yara-python`、`error: command 'gcc' failed`。你以为是包坏了，其实是**系统缺构建工具链**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "yara-python 对**新版 Python（3.13/3.14）没有预编译 wheel**，pip 只能从源码构建；源码构建需要 gcc/make/python3-dev，缺任何一个都会在中途失败：",
          "",
          "pip 安装依赖时有两级供应：**预编译 wheel**（二进制，pip 直接下载解包，不碰编译器）和**源码构建**（没有 wheel 时 pip 下载源码包，在本机调用 gcc 编译）。",
          "",
          "yara-python 这类带 C 扩展的包，wheel 是否可用取决于“包作者是否针对你的 Python 版本发布了 wheel”——Python 3.13/3.14 太新，作者还没发对应 wheel，pip 就退回源码构建。",
          "",
          "源码构建是一条完整的工具链：gcc（编译）→ make（构建编排）→ python3-dev（提供 Python.h 头文件），缺任何一个都在对应环节失败。`error: command 'gcc' failed` 是“编译阶段”的通用报错头，真正的缺什么要看**日志末尾**（末尾是第一个真实的编译错误）。",
          "",
          "所以排查顺序固定：先看 Python 版本有没有对应 wheel（省事路径）→ 没有就补工具链（build-essential + python3-dev）→ 再失败就看完整日志末尾按名补缺。",
          "",
          "| 前置条件 | 作用 |",
          "|---|---|",
          "| build-essential（gcc/make） | 编译 C 扩展 |",
          "| python3-dev | Python 头文件（Python.h） |",
          "| 对应版本的预编译 wheel | 有的话根本不用构建（如 cp312 多数平台有轮子） |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：Debian/Ubuntu 装齐构建依赖后重试安装",
          "sudo apt update && sudo apt install -y build-essential python3-dev && pip install skillspector",
          "```",
          "",
          "装完再跑安装命令；构建再次失败就按下面第 3 步看真实编译错误。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 装构建依赖 — **必做**",
          "",
          "```bash",
          "sudo apt install build-essential python3-dev",
          "```",
          "",
          "### 2. 优先用有预编译 wheel 的 Python — **必做**",
          "",
          "系统 Python 官方版本（如 cp312）多数平台有 yara-python 轮子，构建场景少；非要新版 Python 才走源码构建。",
          "",
          "### 3. 构建再次失败：看完整日志末尾 — **必做**",
          "",
          "```bash",
          "pip install skillspector 2>&1 | tail -40",
          "```",
          "",
          "末尾是真实编译错误：缺头文件/库就按名补装（如 `libyara-dev` 或对应 -dev 包）。"
        ],
        "怎么确认修好了": [
          "",
          "安装日志出现构建成功标记（实测输出形态）：",
          "",
          "```text",
          "Building wheel for yara-python ... Successfully built",
          "```",
          "",
          "`skillspector --version` 输出 `SkillSpector v2.10.0`（实测闭环 v3）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Python 3.14 | yara-python 4.5.4 无 cp314 wheel，实测真实走源码构建并成功（本环境工具链齐全） |",
          "| Python 3.12 | 多数平台有预编译 wheel，构建场景少 |",
          "| 缺 gcc | `error: command 'gcc' failed` → build-essential |",
          "| 缺头文件 | 按编译错误末尾的包名补装对应 -dev 包 |",
          "| Windows | 走 MSVC 工具链，问题形态不同，本市场未实测 |",
          ""
        ],
        "症状": [
          "",
          "",
          "pip 安装时报 `Failed building wheel for yara-python` / `error: command 'gcc' failed`。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "yara-python 对新版 Python（3.13/3.14）没有预编译 wheel，会从源码构建；系统缺 build 工具链（gcc/make/python3-dev）时构建失败。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 装构建依赖：`sudo apt install build-essential python3-dev`。",
          "2. 用系统 Python 官方版本的预编译 wheel（如 cp312 多数平台有轮子，构建场景少）。",
          "3. 构建再次失败时看完整日志末尾的真实编译错误（缺头文件/库按名补装）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN 文库（Failed building wheel → build-essential python3-dev，2026-07）。",
          "- 本市场实测：Python 3.14 下 yara-python 4.5.4 无 cp314 wheel，真实走了源码构建路径并成功（闭环 v3：`Building wheel for yara-python ... Successfully built`）——证明构建路径存在且本环境工具链齐全；缺工具链的机器会在此步失败。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-2",
        "agent": "https://trove.xin/agent/problems/skillspector-2"
      }
    },
    {
      "id": "skillspector-3",
      "title": "MCP 模式 stdio 挂起（可选依赖缺失）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "你用 MCP server 模式（`skillspector mcp`）时进程**挂起无输出**，像死机一样，没有报错、没有日志。你以为是工具坏了，其实是 MCP 模式依赖的可选组件没装全（Issue #199 场景）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "MCP 模式依赖**可选组件**，默认 `pip install skillspector` 安装不完整；缺组件时 stdio 会话协议初始化卡住，**挂起时无任何报错**。而主扫描路径 `skillspector scan` 不需要 MCP 也能完成扫描。",
          "",
          "MCP 的 stdio 模式本质是“子进程 + 标准输入输出”的 JSON-RPC 会话：MCP client 把 `skillspector mcp` 启动为子进程，双方通过 stdin/stdout 逐行交换 JSON 消息，协议第一步是初始化握手（initialize 请求 → 响应）。",
          "",
          "可选依赖缺失时，子进程在 import 阶段就失败了，而失败发生在“协议还没开始”的位置——没有请求存在，自然没有任何报错可输出，client 只能一直等，表现就是挂起无输出。",
          "",
          "所以“无报错”恰恰是关键线索：挂起 + 完全无输出 = 大概率是进程根本没起来（依赖缺失 / import 失败），而不是逻辑死循环。排查顺序因此固定：先 `--help` 验证 CLI 层能正常加载（能输出说明包本体没坏）→ 再判断问题在协议侧 → 补可选依赖；排查期间用 `scan` 主路径绕开 MCP 继续干活。",
          "",
          "| 模式 | 依赖 | 表现 |",
          "|---|---|---|",
          "| `skillspector scan`（主路径） | 基础依赖 | ✅ 正常（本市场实测可用） |",
          "| `skillspector mcp`（MCP server） | 额外可选依赖 | ❌ 缺依赖时挂起无输出 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先确认 CLI 层正常，再按项目文档装全 MCP 可选依赖后重启",
          "skillspector mcp --help && pip install -U skillspector[mcp]",
          "```",
          "",
          "`--help` 能正常输出说明 CLI 层没问题，问题在会话协议侧；可选依赖包名以项目文档为准。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. Ctrl+C 退出挂起进程 — **必做**",
          "",
          "挂起无输出时先退出，别干等。",
          "",
          "### 2. 确认 CLI 层正常 — **必做**",
          "",
          "```bash",
          "skillspector mcp --help",
          "```",
          "",
          "能正常输出 = CLI 层正常，问题在 MCP 会话协议侧（可选依赖缺失）。",
          "",
          "### 3. 安装 MCP 相关可选依赖 — **进阶**",
          "",
          "按项目文档安装后重启 MCP 模式（包名以文档为准，示例 `skillspector[mcp]`）。",
          "",
          "### 4. 排查期间用主路径 — **排查**",
          "",
          "```bash",
          "skillspector scan <目标目录>",
          "```",
          "",
          "不需要 MCP 也能完成扫描（本市场实测构造样本检出）。"
        ],
        "怎么确认修好了": [
          "",
          "MCP 模式不再挂起（预期形态）：",
          "",
          "```text",
          "skillspector mcp",
          "→ 正常启动，stdout 出现 MCP 协议握手/就绪输出",
          "```",
          "",
          "或确认主路径可用：`skillspector scan` 正常输出扫描结果。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 依赖装全后 | MCP 模式正常（国外信号 Issue #199 的修复路径） |",
          "| 只做扫描 | 直接用 `skillspector scan`，绕开 MCP |",
          "| 无报错挂起 | 先用 `--help` 分层定位，再补可选依赖 |",
          "| 未实测项 | 本市场未实跑 mcp 模式（指南标注），按 Issue #199 信号整理 |",
          ""
        ],
        "症状": [
          "",
          "",
          "用 MCP server 模式（`skillspector mcp`）时进程挂起无输出。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "MCP 模式依赖可选组件，默认 pip 安装不完整（Issue #199 场景）；stdio 挂起时无报错，像死机。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 先 Ctrl+C 退出，确认依赖：按项目文档安装 MCP 相关可选依赖后重启。",
          "2. 排查期间用 `skillspector scan` 主路径（不需要 MCP 也能完成扫描）。",
          "3. 挂起时检查：`skillspector mcp --help` 是否正常输出（能输出说明 CLI 层正常，问题在会话协议侧）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 国外信号：Issue #199（mcp stdio 挂起，可选 mcp 依赖缺失导致安装不完整）。",
          "- 本市场实测：mcp 模式未实跑（指南标注）；scan 主路径可用（构造样本检出）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-3",
        "agent": "https://trove.xin/agent/problems/skillspector-3"
      }
    },
    {
      "id": "skillspector-4",
      "title": "依赖版本冲突警告",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "安装或运行 skillspector 时，提示与已装包版本冲突（langchain/openai/pydantic 等）。你怕把系统里其它项目的环境搞坏，不敢装。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "skillspector 依赖**较新的 langchain/openai/langgraph 家族**；pip 直接装进系统 Python 时，会与其它项目的旧版本互相覆盖：",
          "",
          "pip 的依赖模型是“一个环境一份包”：同一个 Python 环境里，一个包名只能存在一个版本。新装的包声明了和现有包冲突的版本时，pip 要么覆盖旧版本、要么直接拒绝——它没有“每个项目各用各的版本”的概念。",
          "",
          "这就是 venv 存在的意义：venv 本质上是给这个环境一份独立的 `site-packages` 副本，Python 靠解释器路径找到自己那份，装进 venv 的包只对 venv 可见，系统 Python 的 `pip list` 一个不多。",
          "",
          "所以“怕弄坏系统环境”的正解不是小心翼翼地装，而是根本不在系统环境里装：`python3 -m venv` 一条命令就把“会互相覆盖”变成“互不可见”。判定是否真的在用 venv 也很简单：`which skillspector` 看命令路径，落在 venv 的 bin 目录下就是对的（本市场实测隔离 venv 装 60+ 依赖无冲突）。",
          "",
          "| 场景 | 问题 |",
          "|---|---|",
          "| 系统 Python 直接装 | 新依赖覆盖旧包，其它项目受影响 |",
          "| 已有项目锁文件 | 版本被改写，项目行为变化 |",
          "| 多个 AI 工具共存 | langchain/openai 版本互相拉扯 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：隔离 venv 安装，不污染系统 Python（本市场实测方式）",
          "python3 -m venv ~/skillspector-venv && ~/skillspector-venv/bin/pip install skillspector && ~/skillspector-venv/bin/skillspector --version",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 建隔离环境 — **必做**",
          "",
          "```bash",
          "python3 -m venv ~/skillspector-venv",
          "```",
          "",
          "### 2. 装进 venv — **必做**",
          "",
          "```bash",
          "~/skillspector-venv/bin/pip install skillspector",
          "```",
          "",
          "### 3. 用 venv 内命令 — **必做**",
          "",
          "```bash",
          "~/skillspector-venv/bin/skillspector scan <目标目录>",
          "```",
          "",
          "### 4. 冲突严重时用 pipx/uv — **必做**",
          "",
          "用 pipx/uv 管理独立环境，每个工具一个环境，互不干扰。"
        ],
        "怎么确认修好了": [
          "",
          "venv 内安装完成（实测输出形态）：",
          "",
          "```text",
          "Successfully installed ... skillspector-2.10.0",
          "```",
          "",
          "`skillspector --version` 输出 `SkillSpector v2.10.0`（本市场实测：隔离 venv 安装 60+ 依赖无冲突）；系统 Python 未受影响（`pip list` 里无新增）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| venv 隔离 | 实测 60+ 依赖无冲突，系统 Python 不受影响 |",
          "| 升级已有包 | 升级前备份项目锁文件（requirements/uv.lock 等） |",
          "| pipx / uv | 每个工具独立环境，适合冲突严重场景 |",
          "| 已有项目受影响 | 用 venv/pipx 隔离后，原项目锁文件无需改动 |",
          ""
        ],
        "症状": [
          "",
          "",
          "安装或运行时提示与已装包版本冲突（langchain/openai/pydantic 等）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "skillspector 依赖较新的 langchain/openai/langgraph 家族；与系统里其它项目的旧版本互相覆盖。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 隔离安装：`python3 -m venv <dir>` 后装进 venv（本市场实测方式），不污染系统 Python。",
          "2. 或按提示升级对应包；升级前备份项目锁文件（requirements/uv.lock 等）。",
          "3. 冲突严重时用 pipx/uv 管理独立环境。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN 文库（依赖版本冲突，2026-07）。",
          "- 本市场实测：隔离 venv 安装 60+ 依赖无冲突（闭环 v3：`Successfully installed ... skillspector-2.10.0`）；系统 Python 未受影响。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-4",
        "agent": "https://trove.xin/agent/problems/skillspector-4"
      }
    },
    {
      "id": "skillspector-5",
      "title": "风险评分 >50 输出 DO NOT INSTALL",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "扫一个技能包，报告里出现 `DO NOT INSTALL` 或很高的风险分数。你以为工具坏了、或者把什么好包误杀了——其实这是**评分逻辑在正常工作**，不是故障。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "skillspector 的分级逻辑（CSDN 源码解析确认）：**风险评分 >50 → DO NOT INSTALL；中低分 → CAUTION/建议**。分数只是汇总信号，真正要判断的是报告 Issues 列表里的具体条目（如 SC2 外部脚本拉取、TM 链滥用等）。",
          "",
          "安全扫描器的输出是**两层结构**：底层是规则检出（每条 Issue：命中什么规则、severity、confidence、位置），上层是把检出汇总成一个分数（score）。分数是**压缩过的信号**——它把几十条检出压成一个数字，方便人快速决策，但压缩会丢信息：两条同样 40 分的报告，可能一条是“2 个 HIGH 检出”，另一条是“20 个 LOW 检出”，风险性质完全不同。",
          "",
          "所以判断的正确顺序是“**先明细后分数**”：分数 >50 是“别装”的快速提示，但真正决定“能不能装”的是 Issues 里的具体条目——HIGH severity + 高 confidence 的外部脚本拉取（curl|bash）是硬风险，中低分的噪声项可以人工核对行号后放行。",
          "",
          "还有一点：评分器对内容敏感，同一技能不同版本检出可能不同，所以别拿“上次的分”当“这次的结论”，每次以当次报告的 Issues 为准。",
          "",
          "| 分数段 | 输出 | 含义 |",
          "|---|---|---|",
          "| >50 | DO NOT INSTALL | 不建议装（看具体 Issues） |",
          "| 中低分 | CAUTION / 建议 | 有风险点，人工判断 |",
          "| 同一技能不同版本 | 分数可能不同 | 评分器对内容敏感 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先看 Issues 明细再下结论，别只看分数",
          "skillspector scan <技能目录> --json | python3 -c \"import json,sys; d=json.load(sys.stdin); [print(i['severity'], i['confidence'], i['rule'], i['location']) for i in d.get('issues',[])]\"",
          "```",
          "",
          "输出 Severity/Confidence 明细，据此判断是否真的不能装。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 看报告 Issues 列表 — **必做**",
          "",
          "看每条 Issue 的 Severity/Confidence，是不是真的命中危险模式（外部脚本拉取、token 泄漏等）。",
          "",
          "### 2. 结合 baseline 抑制已知项 — **必做**",
          "",
          "分数受评分器影响，同一技能不同版本分数可能不同；用 baseline 抑制已知项后再看。",
          "",
          "### 3. 拿不准时人工检查 — **排查**",
          "",
          "```bash",
          "grep -n \"检出规则名\" SKILL.md",
          "```",
          "",
          "报告会给出检出行号，人工看 SKILL.md 对应行判断真伪。"
        ],
        "怎么确认修好了": [
          "",
          "扫描报告结构完整、明细可读（本市场实测形态）：",
          "",
          "```text",
          "Score 40/100 MEDIUM + 2 个 HIGH 检出（SC2 90% / TM2 70%）——构造样本（curl|bash + token 泄漏）",
          "```",
          "",
          "此时 40 分 <50 不输出 DO NOT INSTALL，但 HIGH 检出条目仍在 Issues 里——以条目为准。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 分数 >50 | DO NOT INSTALL，先看 Issues 明细 |",
          "| 中低分 | CAUTION/建议，人工判断 |",
          "| baseline | 抑制已知项后分数变化属正常 |",
          "| 报告行号 | 人工核对 SKILL.md 对应行 |",
          "| 未实测项 | baseline 命令本市场未实测 |",
          ""
        ],
        "症状": [
          "",
          "",
          "扫描结果出现 `DO NOT INSTALL` 或高风险分数，用户以为工具坏了。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "这是评分逻辑不是故障：风险评分 >50 → DO NOT INSTALL；中低分 → CAUTION/建议（CSDN 源码解析确认的分级逻辑）。扫描器检出的问题条目（SC2 外部脚本拉取、TM 链滥用等）才是判断依据。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 看报告 Issues 列表的 Severity/Confidence，判断是否真的不能装。",
          "2. 分数受评分器影响，同一技能不同版本分数可能不同；结合 baseline 抑制已知项。",
          "3. 拿不准时人工检查 SKILL.md 中检出行（报告给出行号）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（风险评分>50 DO NOT INSTALL，2026-06；源码解析）。",
          "- 本市场实测：构造样本（SKILL.md 含 curl|bash + token 泄漏）→ Score 40/100 MEDIUM + 2 个 HIGH 检出（SC2 90% / TM2 70%），报告结构完整。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-5",
        "agent": "https://trove.xin/agent/problems/skillspector-5"
      }
    },
    {
      "id": "skillspector-6",
      "title": "semantic_* 分析器警告（缺 API key）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "skillspector"
      ],
      "evidence": "site docs#6;local repro log;local repro log;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`skillspector --version` 或 scan 时，输出 `WARNING [skillspector.graph] Skipping analyzer semantic_developer_intent: required API key is missing`。你以为是配置坏了、或者功能不全——其实**没配 key 时自动跳过是正常行为**，不是错误。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "LLM 语义分析器需要 `OPENAI_API_KEY`（或 SKILLSPECTOR_PROVIDER 配置）才能跑；没有 key 时它自动跳过，只输出一条 WARNING：",
          "",
          "| 分析器类型 | 依赖 | 没 key 时 |",
          "|---|---|---|",
          "| 规则检测（YARA/模式） | 无 | ✅ 完全可用 |",
          "| semantic_*（LLM 语义） | OPENAI_API_KEY | ⏭ 跳过 + 警告 |",
          "",
          "警告不影响退出码与报告生成（本市场实测：3 条语义跳过警告，exit 0，扫描正常）。",
          "",
          "skillspector 的扫描器是**两级架构**：规则检测（YARA/模式匹配）在本地跑，不依赖外部服务；语义分析（semantic_*）要调用 LLM——它把“这段技能内容有没有恶意意图”交给大模型判断，而调用 LLM 需要 API key。没有 key 时的行为是**降级而非报错**：扫描器检测到没 key，跳过语义分析器、打一条 WARNING、继续跑完规则检测。",
          "",
          "这是“可选依赖”的标准设计：核心功能（规则检测）不依赖外部服务，增强功能（语义分析）按可用性启用。",
          "",
          "WARNING 不是错误——退出码和报告都正常生成，只是报告里少了语义分析的维度。判断“扫描结果完整吗”也有固定口径：**看 WARNING 说的是什么**（跳过的是增强项还是核心项）——跳过语义分析不影响规则检测的结论，但如果你依赖语义分析（比如要判断恶意意图），就得先配 key 再扫。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：不需要语义分析就直接静态扫（规则检测不依赖 LLM）",
          "skillspector scan <目标目录> --no-llm",
          "```",
          "",
          "要语义分析就把 `OPENAI_API_KEY` 配上，或按文档设 `SKILLSPECTOR_PROVIDER`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 不配 key：静态扫描完全可用 — **必做**",
          "",
          "```bash",
          "skillspector scan <目标目录> --no-llm",
          "```",
          "",
          "规则检测（YARA/模式）不依赖 LLM，该检出的照样检出。",
          "",
          "### 2. 要语义分析：配 key — **排查**",
          "",
          "```bash",
          "export OPENAI_API_KEY=你的key",
          "```",
          "",
          "或按项目文档设置 `SKILLSPECTOR_PROVIDER`。",
          "",
          "### 3. 警告可安全忽略 — **必做**",
          "",
          "WARNING 不影响退出码与报告生成；`--version` 和 scan 照常工作（实测 exit 0）。"
        ],
        "怎么确认修好了": [
          "",
          "扫描正常完成（实测输出形态）：",
          "",
          "```text",
          "skillspector scan <目录>",
          "→ 3 条 semantic 跳过警告 + 扫描结果正常输出，exit 0",
          "```",
          "",
          "或配 key 后警告消失，semantic 分析器参与评分。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 无 key（默认） | 警告 + 跳过，功能不受影响（实测） |",
          "| 有 key | 语义分析参与，评分可能变化 |",
          "| Windows / macOS | 环境变量配置方式相同 |",
          "| 多个工具共用 key | 属配置面注意项，非写面冲突 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`skillspector --version` / scan 输出：",
          "",
          "```",
          "WARNING [skillspector.graph] Skipping analyzer semantic_developer_intent: required API key is missing",
          "```",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "LLM 语义分析器需要 OPENAI_API_KEY（或 SKILLSPECTOR_PROVIDER 配置）；没 key 时自动跳过——不是错误。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 不配 key：静态扫描完全可用（`--no-llm`），规则检测（YARA/模式）不依赖 LLM。",
          "2. 要语义分析：配 `OPENAI_API_KEY` 环境变量或按文档设 SKILLSPECTOR_PROVIDER。",
          "3. 警告可安全忽略，不影响退出码与报告生成。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 本市场实测：安装与运行均输出 3 条 semantic 跳过警告，exit 0，扫描正常（+ 闭环 v3）。",
          ""
        ],
        "本市场实测安装 / 卸载（2026-08-28）": [
          "",
          "安装（manifest install.cmd，隔离 venv /tmp/l34-sp/venv，Python 3.14.4）：",
          "",
          "```",
          "[install] curl -sL wheel（gh-proxy，468K）→ pip install -i 清华镜像 skillspector-2.10.0-py3-none-any.whl",
          "依赖：boto3/httpx/langchain-*/langgraph/openai/pydantic/yara-python（源码构建成功）等 60+ 包",
          "SkillSpector v2.10.0（3 条 semantic 跳过警告，exit 0）",
          "[install] exit=0",
          "```",
          "",
          "卸载（manifest uninstall.cmd + 修正后的 verify）：",
          "",
          "```",
          "[uninstall] pip uninstall -y skillspector → Successfully uninstalled skillspector-2.10.0",
          "[uninstall] 验证: skillspector removed: OK（fresh shell，verify exit=0）",
          "```",
          "",
          "- 完整日志：实测记录（本地留存）。",
          "- PyPI 缺失排查：实测记录（本地留存）。",
          "- 扫描冒烟：实测记录（本地留存）（构造样本检出 2 个 HIGH，只读扫描确认）。",
          "- 未能实测：MCP server 模式实跑、LLM 语义分析（无 API key）、baseline 命令。",
          ""
        ],
        "冲突标注": [
          "",
          "- possible-compatible（推断态）：全部收录条目——skillspector 只读扫描（扫描前后目录零新增）、无 daemon、无配置写面、无端口；CLI 面与任何条目共存无共享写面（矩阵对子）。",
          "- 备注：LLM 语义分析如需与其它工具共用 API key 环境变量，属配置面注意项（非写面冲突）。",
          ""
        ],
        "名称混淆": [
          "",
          "- 大小写：项目名 SkillSpector（NVIDIA 开源），命令 `skillspector`。",
          "- 渠道：PyPI 上无此包（见症状 1），检索/安装认准 GitHub 仓库 NVIDIA/SkillSpector 与 release 资产。",
          "- 与其它技能安全/检查类工具区分（如 agent-skills 等技能包，功能不同）。",
          ""
        ],
        "判据（验证状态）": [
          "",
          "- 症状清单：",
          "  - [x] pip install 找不到包 → 发行渠道 = GitHub Release wheel（本市场三路复核 + 闭环实测）",
          "  - [x] Failed building wheel for yara-python → build-essential python3-dev（CSDN 文库 2026-07；本市场源码构建路径实测）",
          "  - [x] 可选 mcp 依赖缺失导致 stdio 挂起（Issue #199）",
          "  - [x] 依赖版本冲突警告（CSDN 文库 2026-07；venv 隔离解法实测）",
          "  - [x] 风险评分>50 输出 DO NOT INSTALL（CSDN 2026-06；评分逻辑说明）",
          "  - [x] semantic_* 分析器缺 key 警告（本市场实测）",
          "- 实测步骤：",
          "  - [x] 安装验证（manifest install.cmd 修正后：wheel 直装 + 依赖走镜像 → SkillSpector v2.10.0 exit 0）",
          "  - [x] 卸载验证（manifest uninstall.cmd：pip uninstall → verify 零残留 exit 0）",
          "  - [x] 网络/镜像场景（gh-proxy wheel 下载 + 清华镜像依赖；）",
          "  - [x] 未能实测项：MCP 模式实跑、LLM 语义分析（无 key）、baseline",
          ""
        ],
        "验收": [
          "",
          "- [x] 症状 → 原因 → 解法 → 验证证据 四段齐全（6 症状）",
          "- [x] 名称混淆已点名（大小写；PyPI 无包；渠道认准 GitHub）",
          "- [x] check-words HARD 0",
          "- [x] validate 通过",
          "- [x] 未碰 GitHub / 未用任何账号"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/skillspector-6",
        "agent": "https://trove.xin/agent/problems/skillspector-6"
      }
    },
    {
      "id": "sms-api-rate-limit",
      "title": "短信接口被狂刷（短信费用飙升/接口被打垮，怎么限流止损）",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "nginx"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/zrui-xyu/p/22612063",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "短信接口被脚本/恶意流量狂刷：短时间内大量请求、短信费用飙升、接口被打垮。泛化到任意业务接口就是：同一 IP/同一手机号高频请求、代理 IP 池轮换刷量、验证码接口被撞库。一次被刷就是真金白银损失。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "接口没有频率限制与黑名单：短信/验证码接口天然可被无限调用（无频次、无来源限制）。紧急止损手段（熔断/降级/黑名单/限流）缺失或**配置无效**——实测发现 `limit_req` 与 `return` 写在同一 location 时被短路，配置\"看似有\"实则无。",
          "",
          "| 防护层 | 手段 | 实测状态 |",
          "|---|---|---|",
          "| 紧急止损 | 测试模式/熔断、Nginx deny、全局限流 | ✅ nginx deny 实测 403 |",
          "| Nginx 限流 | `limit_req_zone` + `limit_req` | ✅ 静态/反代形态实测生效（503） |",
          "| 业务层 | Redis 频次、布隆过滤器、设备指纹 | ⏳ 本机无 redis/docker，未实测 |",
          "| 监控复盘 | 请求量基线 + 异常告警 | 上线后长期动作 |",
          "",
          "短信/验证码接口被刷的本质是「无状态接口 + 无频次约束」：接口本身不记调用来源与次数，攻击者可以无限调用（撞库/骚扰/刷量），而成本由你承担。防护手段的「配置无效」是独立陷阱：nginx 的 `limit_req` 与 `return` 写在同一 location 时，`return` 直接短路后续指令——配置「看似有」实则无，这是实测复现的坑。",
          "",
          "确定性推论：先验证防护真的生效（实测：nginx deny 403、limit_req 503），不验证的配置等于没有；防护分三层——网络层（nginx deny/限流，可立即上）、业务层（Redis 频次/设备指纹，需依赖）、监控层（基线+告警，长期）；紧急止损用测试模式/熔断最直接（先止血再补防护）；判断路径：被刷先确认「防护是否真的返回预期状态码」，再谈策略。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：nginx 层加 IP 限流（http 块加 zone，location 内启用；超限返回 503）",
          "# http 块：limit_req_zone $binary_remote_addr zone=sms:10m rate=5r/s;",
          "# location 内：limit_req zone=sms burst=10 nodelay;",
          "```",
          "",
          "`burst` = 突发桶容量，`nodelay` = 超限立即拒绝；默认拒绝 503，可 `limit_req_status 429` 改 429。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 紧急止损（黄金 30 分钟） — **必做**",
          "",
          "配置中心把短信接口切\"测试模式\"（返回成功但不真发）或直接熔断；Nginx 层封禁异常 IP（`deny`，实测 403）；无效则临时关停短信功能保核心业务。",
          "",
          "### 2. Nginx 频率限制（实测生效形态） — **必做**",
          "",
          "http 块定义 zone：",
          "",
          "```nginx",
          "limit_req_zone $binary_remote_addr zone=sms:10m rate=5r/s;",
          "```",
          "",
          "location 内启用（反代/静态文件形态均实测生效）：",
          "",
          "```nginx",
          "limit_req zone=sms burst=10 nodelay;",
          "```",
          "",
          "### 3. 业务层防护 — **必做**",
          "",
          "手机号频次限制（Redis `INCR`+`EXPIRE`，过期自动重置）；布隆过滤器快速过滤无效/风险手机号（千万级 12MB）；设备指纹/行为风控；API 签名。（本机无 redis-server 与 docker daemon，未实测，如实标注。）",
          "",
          "### 4. 监控复盘 — **必做**",
          "",
          "按 IP/手机号/设备统计请求量基线，异常自动告警；新风控规则灰度发布。"
        ],
        "怎么确认修好了": [
          "",
          "压测断言（实测输出形态：限流 15 连发）：",
          "",
          "```text",
          "curl 连发 15 次",
          "→ 1×200 + 14×503（rate=2r/s 静态文件形态）",
          "→ 错误日志：limiting requests, excess: 0.974 by zone \"min\"",
          "```",
          "",
          "`deny 127.0.0.1` 后请求 → 403（日志 `access forbidden by rule`）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 静态文件形态 | `limit_req` 实测生效：1×200 + 14×503 |",
          "| proxy_pass 反代形态 | 生产形态实测生效：1×200 + 14×503 |",
          "| deny + return 同 location | **实测失效**（return 短路 preaccess/access 阶段） |",
          "| limit_req + return 同 location | **实测失效**（连发全 200、日志无记录）——文章未写的坑 |",
          "| 业务层 | Redis 令牌桶/布隆过滤器未实测（无 redis-server / docker daemon） |",
          ""
        ],
        "症状": [
          "",
          "短信接口被脚本/恶意流量狂刷：短时间内大量请求、短信费用飙升、接口被打垮；泛化到任意业务接口——同一 IP/同一手机号高频请求、代理 IP 池轮换刷量、验证码接口被撞库。",
          ""
        ],
        "原因": [
          "",
          "接口没有频率限制与黑名单：短信/验证码接口天然可被无限调用（无频次、无来源限制），一次被刷就是真金白银损失；紧急止损手段（熔断/降级/黑名单/限流）缺失或配置无效（见验证证据：`limit_req` 与 `return` 同 location 时被短路，配置\"看似有\"实则无）。",
          ""
        ],
        "解法": [
          "",
          "1. **紧急止损（黄金 30 分钟）**：配置中心把短信接口切\"测试模式\"（返回成功但不真发）或直接熔断；Nginx 层封禁异常 IP（`deny`，实测 403）；网关层开全局限流；无效则临时关停短信功能保核心业务。",
          "2. **Nginx 频率限制（实测生效形态）**：",
          "   ```nginx",
          "   limit_req_zone $binary_remote_addr zone=sms:10m rate=5r/s;   # http 块",
          "   # location 内（反代/静态文件形态均实测生效，超限返回 503）：",
          "   limit_req zone=sms burst=10 nodelay;",
          "   ```",
          "   - `burst` = 突发桶容量，`nodelay` = 超限立即拒绝；默认拒绝状态码 503（可 `limit_req_status 429` 改 429）。",
          "   - **实测坑（文章未写）**：`limit_req`/`deny` 与 `return` 写在同一个 location 里不生效——`return` 在 rewrite 阶段终止请求，跳过 preaccess/access 阶段；`location = /api/sms/send { limit_req ...; return 200 \"ok\"; }` 这类配置等于无限流。接口必须走静态文件/`proxy_pass` 反代形态，或把限流放 `server` 层。",
          "3. **业务层防护**：手机号频次限制（Redis `INCR`+`EXPIRE`，过期自动重置）；布隆过滤器快速过滤无效/风险手机号（千万级 12MB）；设备指纹/行为风控；API 签名。本机无 redis-server 与 docker daemon，未实测（如实标注）。",
          "4. **监控复盘**：按 IP/手机号/设备统计请求量基线，异常自动告警；新风控规则灰度发布。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，nginx/1.28.3 nginx-light + python3，纯本地零网络）：",
          "  1. 对照：无限流接口 15 连发全 200。",
          "  2. `limit_req zone=min rate=2r/s` + 静态文件：15 连发 = 1×200 + 14×503，错误日志记录 `limiting requests, excess: 0.974 by zone \"min\"`。",
          "  3. `limit_req rate=2r/s` + `proxy_pass` 反代：15 连发 = 1×200 + 14×503（生产形态正常生效）。",
          "  4. `deny 127.0.0.1`：请求 403，日志 `access forbidden by rule`。",
          "  5. **坑**：`limit_req + return 200` 同 location 连发全 200、日志无记录；`deny + return` 同样失效——`return` 短路 preaccess/access 阶段（第一版配置实测发现，修正后才有 2/3/4 的结果）。",
          "- Redis/Lua 令牌桶、布隆过滤器：未实测（本机无 redis-server；Docker daemon 未运行，镜像拉取受限）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/sms-api-rate-limit",
        "agent": "https://trove.xin/agent/problems/sms-api-rate-limit"
      }
    },
    {
      "id": "ssh-key-permissions",
      "title": "SSH 报 UNPROTECTED PRIVATE KEY FILE / Permissions ... are too open（私钥权限过宽）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "ssh"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/Azure/azure-cli-extensions/issues/6408",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "用 `ssh -i 私钥` 登录时报整屏 `WARNING: UNPROTECTED PRIVATE KEY FILE!`，`Permissions 0644 for '/path/id_rsa' are too open`，最后 `Permission denied (publickey)`。密钥文件本身没问题，你也没改过任何东西——只是**文件权限太宽**被 OpenSSH 拒了。",
          "",
          "常见于从压缩包/网盘拷来的密钥、Windows 工具生成的密钥（0644）、或 WSL 挂载 Windows 盘的密钥（chmod 看着生效实际无效）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "OpenSSH 客户端加载私钥时强制要求：私钥**不能被其他用户/组读取**（必须是 600/700 级别）。一旦 group/other 有读权限（644/640/664 等），直接忽略该密钥并拒绝用其认证——这是防私钥泄露的安全设计，不是故障。",
          "",
          "- 注意该检查在**认证阶段**才触发（需先建立 TCP 连接），所以连不上服务器时看不到这个警告。",
          "- WSL 里挂载 `/mnt/c` 的密钥：drvfs 文件系统不支持 chmod，`chmod 600` 看着生效实际无效——必须把密钥复制进 WSL 再改，或用 icacls 在 Windows 侧修。",
          "",
          "> 实测环境：WSL Ubuntu，OpenSSH 10.2p1，本机临时 sshd（127.0.0.1:2222），2026-08-29。",
          "",
          "OpenSSH 客户端在认证阶段强制校验私钥文件权限：group/other 有读权限（644/640/664 等）就**静默忽略**该密钥（`Load key ... bad permissions`），这是防私钥泄露的安全设计，不是故障——私钥就是密码，任何人都能读的密码等于没有密码。检查发生在认证阶段（建立 TCP 连接后），所以连不上服务器时看不到警告。",
          "",
          "WSL 挂载盘（/mnt/c）的 drvfs 不支持 chmod：`chmod 600` 表面成功实际无效（文件仍按挂载默认权限暴露）。",
          "",
          "确定性推论：`Load key ... bad permissions` = 权限不对，`chmod 600 ~/.ssh/id_xxx` 即修（目录也建议 700）；WSL 里密钥放 `~/.ssh/`（WSL 文件系统）而不是 /mnt/c；`ssh -v` 看详细加载过程定位「忽略」原因；判断路径：警告出现在认证阶段 = 权限问题，不是网络/服务器问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：私钥 600、目录 700（Linux/macOS/WSL 原生文件系统）",
          "chmod 600 ~/.ssh/id_ed25519 && chmod 700 ~/.ssh && ssh -i ~/.ssh/id_ed25519 user@host",
          "```",
          "",
          "Windows 原生 OpenSSH 用：`icacls \"私钥路径\" /inheritance:r /grant:r \"%USERNAME%:R\"`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认是权限问题 — **必做**",
          "",
          "```bash",
          "ls -l ~/.ssh/id_ed25519",
          "```",
          "",
          "权限是 `-rw-r--r--`（644）或 `-rw-r-----`（640）→ 就是这个问题（实测：644 和 640 都触发警告，exit 255）。",
          "",
          "### 2. 改私钥权限 — **必做**",
          "",
          "```bash",
          "chmod 600 ~/.ssh/id_ed25519",
          "```",
          "",
          "公钥 644 无妨；`~/.ssh` 目录本身别 777（`chmod 700 ~/.ssh`）。",
          "",
          "### 3. Windows 工具/WSL 挂载盘的密钥 — **必做**",
          "",
          "- WSL 访问 `/mnt/c` 上的密钥：先 `cp` 到 WSL 内（如 `~/.ssh/`）再 `chmod 600`；",
          "- Windows 原生 OpenSSH：`icacls \"私钥路径\" /inheritance:r /grant:r \"%USERNAME%:R\"`；",
          "- 工具生成的临时密钥（az bastion ssh 等 /tmp 密钥）：先 chmod 600 再用。",
          "",
          "### 4. 批量修复注意 — **必做**",
          "",
          "`chmod 600 ~/.ssh/*` 会误伤公钥（公钥 600 也行，只是没必要）；精确一点只改无 `.pub` 的文件。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "ssh -i ~/.ssh/id_ed25519 user@host",
          "```",
          "",
          "不再出现 `UNPROTECTED PRIVATE KEY FILE` 警告、能免密登录即成功。实测：私钥 600 → 登录成功（exit 0）；644/640 → 警告 + Permission denied（exit 255）；改回 600 → 恢复成功。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Linux/macOS/WSL 原生文件 | `chmod 600 私钥` + `chmod 700 ~/.ssh` |",
          "| Windows 原生 OpenSSH | chmod 不适用，用 `icacls ... /inheritance:r /grant:r \"%USERNAME%:R\"` |",
          "| WSL 访问 /mnt/c 密钥 | drvfs 不支持 chmod，复制进 WSL 再改，或用 icacls |",
          "| 工具临时密钥 | 先 chmod 600 再用；连不上服务器时看不到警告（检查在认证阶段） |",
          ""
        ],
        "症状": [
          "",
          "用 `ssh -i 私钥` 登录时整屏警告：",
          "",
          "```",
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          "@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @",
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          "Permissions 0644 for '/path/id_rsa' are too open.",
          "It is required that your private key files are NOT accessible by others.",
          "This private key will be ignored.",
          "Load key \"/path/id_rsa\": bad permissions",
          "root@host: Permission denied (publickey,keyboard-interactive).",
          "```",
          "",
          "特征：密钥文件本身没问题，是**文件权限**被 ssh 拒绝（组/其他人可读就报）；",
          "常见于从压缩包/网盘拷来的密钥、Windows 工具生成的密钥（azure-cli-extensions#6408 就是工具生成 0644 密钥）、",
          "或 WSL 里挂载 Windows 盘的密钥（chmod 无效）。",
          ""
        ],
        "原因": [
          "",
          "OpenSSH 客户端加载私钥时强制要求：私钥不能被其他用户/组读取（必须是 600/700 级别）。",
          "一旦 group/other 有读权限（644/640/664 等），直接忽略该密钥并拒绝用其认证——这是防私钥泄露的安全设计，不是故障。",
          "注意：该检查在**认证阶段**才触发（需要先建立 TCP 连接），所以连不上服务器时看不到这个警告。",
          ""
        ],
        "解法": [
          "",
          "1. **改权限（Linux/macOS/WSL）**：`chmod 600 ~/.ssh/id_ed25519`（私钥），`chmod 644` 公钥、`chmod 700 ~/.ssh`；",
          "2. **Windows 原生 OpenSSH**：chmod 不适用，用 `icacls \"私钥路径\" /inheritance:r /grant:r \"%USERNAME%:R\"`；",
          "3. **WSL 访问 /mnt/c 上的密钥**：drvfs 不支持 chmod（chmod 看着生效实际无效），",
          "   正确做法把密钥复制到 WSL 内（`cp` + `chmod 600`）或用 icacls 在 Windows 侧修；",
          "4. **工具生成的临时密钥**（az bastion ssh / 各类 CLI 的 /tmp 密钥）：先 chmod 600 再用，或反馈工具修复（#6408 场景）；",
          "5. 检查父目录：`~/.ssh` 目录本身别 777（`chmod 700 ~/.ssh`）；",
          "6. 批量修复：`chmod 600 ~/.ssh/*` 会误伤公钥（公钥 644 无妨，600 也行），精确一点只改无 .pub 的文件。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，OpenSSH 10.2p1，本机临时 sshd 端口 2222）：",
          "  - 环境：apt 装 openssh-server，临时 sshd（127.0.0.1:2222、PermitRootLogin prohibit-password、PasswordAuthentication no），ed25519 密钥加入 authorized_keys；",
          "  - 基线：私钥 600 → 免密登录成功（exit 0）；",
          "  - 复现 1：chmod 644 → `UNPROTECTED PRIVATE KEY FILE` + `Load key ... bad permissions` + `Permission denied`（exit 255）；",
          "  - 复现 2：chmod 640（仅组可读）→ 同样警告（exit 255）——规则是「不可被他人读取」，不是只要 owner 可读写；",
          "  - 修复：chmod 600 → 免密登录恢复成功（exit 0）；",
          "  - 补充：无 sshd 时（连接失败）该警告不出现——检查在认证阶段，需先能连上；",
          "  - 清理：测试 sshd 已杀、端口 2222 空闲、临时密钥与 authorized_keys 已删（本机原 id_ed25519 未动）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/ssh-key-permissions",
        "agent": "https://trove.xin/agent/problems/ssh-key-permissions"
      }
    },
    {
      "id": "superpowers-1",
      "title": "/plugin install 报 fatal: Failed to connect to github.com port 443",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#1;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Claude Code 里执行 `/plugin install superpowers@claude-plugins-official` 或 `@superpowers-marketplace`，报 `fatal: unable to access 'https://github.com/obra/superpowers.git/': Failed to connect to github.com port 443 after 2045 ms`。",
          "",
          "**应用内配了全局代理也不通**——未开 TUN 时 git 子进程不走代理。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "上游 Claude Code 插件市场的安装路径 = **git clone GitHub 仓库**（市场网站不提供 zip 下载）；国内网络直连 github.com:443 不稳定/被阻断；应用级代理**不覆盖 git 子进程**（除非 TUN/透明代理或给 git 显式配代理）。",
          "",
          "| 通道 | 状态（实测） |",
          "|---|---|",
          "| codeload tarball | ✅ 可达，1.7~1.9s |",
          "| github.com:443 直连 | ❌ 不稳定/被阻断 |",
          "| raw.githubusercontent.com | ❌ 超时（非安装路径，避免使用） |",
          "| gh-proxy 加速 | ✅ 实测可用（7.8s） |",
          "",
          "安装流程的本质是“**CLI 程序调用 git 子进程**”：`/plugin install` 时 CLI 自己执行 `git clone`，下载不是由 CLI 的网络栈完成，而是由它 fork 出的 git 子进程完成。",
          "",
          "应用级代理（App 设置里的代理开关）只作用于**应用自己的网络栈**（HTTP 客户端走的代理），git 子进程是独立的程序，有自己的配置（`~/.gitconfig` 的 http.proxy 等）——除非代理是 TUN/透明模式（接管整个系统的网络流量），否则 git 子进程根本不知道有代理这回事，仍然直连。",
          "",
          "这就是“开了代理还是失败”的机制原因。三个对策对应三个层面：**给 git 显式配代理**（`git config --global http.proxy ...`，让子进程走代理）；**换通道**（codeload tarball 直连可达，用 `curl` 下载压缩包——curl 由 CLI 自己执行，应用级代理也生效）；**gh-proxy 加速**（内容层面替换域名）。",
          "",
          "判断路径固定：先看“卡在哪个环节”（clone = git 子进程直连），再选对应层面的对策。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先探路——codeload 可达就走 tarball 直装（实测 1.7~1.9s）",
          "curl -sI https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0",
          "```",
          "",
          "探测通过后按「一步步来」第 2 步 tarball 安装；或给 git 显式配代理重试。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先探路再选路 — **必做**",
          "",
          "```bash",
          "curl -sI https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0",
          "```",
          "",
          "codeload 可达就走 tarball 直装（本环境 1.7~1.9s）。",
          "",
          "### 2. tarball 直装（实测 PASS） — **必做**",
          "",
          "```bash",
          "curl -sL https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 | tar xz",
          "```",
          "",
          "实测：codeload tarball 544K / 2.1s → 安装 PASS。",
          "",
          "### 3. 给 git 显式配代理后重试 — **必做**",
          "",
          "```bash",
          "git config --global http.proxy http://127.0.0.1:<端口>",
          "```",
          "",
          "或开 TUN/透明代理。",
          "",
          "### 4. 加速通道 — **必做**",
          "",
          "gh-proxy 类加速（2026-08-28 实测可用，7.8s），或用国内镜像站下载 zip 后离线安装；再不行按本地路径方式接入（见症状 6 条目）。"
        ],
        "怎么确认修好了": [
          "",
          "安装流程走完（实测形态）：",
          "",
          "```text",
          "curl 探路 codeload → HTTP 200",
          "tarball 解压后插件目录结构完整 → 安装 PASS",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 应用全局代理 | 不覆盖 git 子进程，未开 TUN 时不生效（CSDN 案例） |",
          "| 显式 git 代理 | `git config --global http.proxy` 后重试 |",
          "| gh-proxy | 实测 7.8s 可用 |",
          "| codeload | 实测 544K / 2.1s 稳定 |",
          "| 离线兜底 | 本地路径方式接入（见症状 6 条目） |",
          ""
        ],
        "症状": [
          "",
          "",
          "Claude Code 中执行 `/plugin install superpowers@claude-plugins-official` 或 `@superpowers-marketplace` 报：",
          "",
          "```",
          "fatal: unable to access 'https://github.com/obra/superpowers.git/': Failed to connect to github.com port 443 after 2045 ms",
          "```",
          "",
          "应用内配了全局代理也不通（未开 TUN 时 git 子进程不走代理）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "上游 Claude Code 插件市场的安装路径 = git clone GitHub 仓库（市场网站不提供 zip 下载）；国内网络直连 github.com:443 不稳定/被阻断；应用级代理不覆盖 git 子进程（除非 TUN/透明代理或给 git 显式配代理）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 先探路再选路：`curl -sI https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0`——codeload 可达就走 tarball 直装（命令见「本市场实测」一节，本环境 1.7~1.9s）。",
          "2. 给 git 显式配代理后重试：`git config --global http.proxy http://127.0.0.1:<端口>`；或开 TUN/透明代理。",
          "3. 走加速：gh-proxy 类加速（2026-08-28 实测可用，7.8s），或用国内镜像站下载 zip 后离线安装（见症状 2）。",
          "4. 离线兜底：按「症状 6」的本地路径方式接入。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：[CSDN m0_58528895/161265224](https://blog.csdn.net/m0_58528895/article/details/161265224)（2026-05）——443 案例全文：`@claude-plugins-official` / `@superpowers-marketplace` 均失败，全局代理不通、未试 TUN。",
          "- 本市场实测 2026-08-28：GitHub 源码下载 200 / 1.91s（站内文档；2026-08-27 基线 1.73s）；raw.githubusercontent.com 超时（非安装路径，避免使用）。",
          "- 本市场实测安装：codeload tarball 544K / 2.1s → PASS（实测记录（本地留存））。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-1",
        "agent": "https://trove.xin/agent/problems/superpowers-1"
      }
    },
    {
      "id": "superpowers-2",
      "title": "ZIP / tarball 下载超时或中断",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#2",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "从 GitHub 下载 zip / tarball 反复超时、中断，始终拿不到完整压缩包。下载 80% 断掉、重试还是断，你开始怀疑是包太大——其实多半是链路问题。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "到 GitHub 的下载链路（codeload/github.com）不稳定或带宽差，大文件传输中途断开。这不是包的问题，是**通道**的问题。",
          "",
          "下载的本质是“**一个 URL 对应一条通道**”：同一份内容在 GitHub 上有多个出口——tarball 走 codeload、raw 文件走 raw 端点、仓库内容走 git 协议。这些出口的域名、CDN 和链路质量都不一样，在墙内表现差异很大：可能 codeload 稳而 raw 超时（本市场实测正是如此）。",
          "",
          "所以“下载失败”时，第一反应不该是“包太大/网络全坏”，而是**换通道**：tarball 断 → 换 git 浅克隆（走 git 协议，只拉最新提交、体积小）；clone 断 → 换 tarball。两个通道互相独立，坏一个不意味着全坏。验证完整性也有固定方法：解压无报错（tarball 流式解压）或目录结构完整（clone）。",
          "",
          "这背后的通用原则是：**同一资源的多个出口之间互相备份，别在一条坏通道上反复重试**。",
          "",
          "| 通道 | 状态（实测） |",
          "|---|---|",
          "| codeload tarball 直连 | ✅ 544K / 2.1s 稳定（当前可用） |",
          "| raw 端点 | ❌ 探针超时（手册统一避开 raw 路径） |",
          "| 大文件 | 中途断开风险高 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：换 git clone 浅克隆通道（与 tarball 反向互换）",
          "git clone --depth 1 --branch v6.3.0 https://github.com/obra/superpowers.git",
          "```",
          "",
          "浅克隆只拉最新提交，体积小、成功率更高。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 换通道 — **必做**",
          "",
          "tarball 下载断 → 改 git clone 浅克隆；clone 断 → 改 tarball。两者互换常常能绕过坏链路。",
          "",
          "### 2. 换网络 / 开代理 — **必做**",
          "",
          "切换网络环境或开代理后重试；错峰重试（避开高峰期）。",
          "",
          "### 3. 离线解压 — **必做**",
          "",
          "换任意可达的镜像站下载 zip → 手动解压 → 按本地路径方式接入；不依赖 zip 完整性的替代 = 只取 skills 目录（tarball 内含）。"
        ],
        "怎么确认修好了": [
          "",
          "下载完整、解压成功（实测形态）：",
          "",
          "```text",
          "curl -sL https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 | tar xz",
          "→ 解压完成无报错（实测 544K / 2.1s）",
          "```",
          "",
          "或 `git clone --depth 1` 正常结束，目录结构完整。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| codeload 直连 | 实测 544K / 2.1s 稳定，当前可用 |",
          "| raw 端点 | 探针超时，统一避开 |",
          "| 大文件 | 换浅克隆或镜像站 |",
          "| 离线 | 镜像站 zip → 手动解压 → 本地路径接入 |",
          ""
        ],
        "症状": [
          "",
          "",
          "从 GitHub 下载 zip / tarball 反复超时、中断，无法拿到完整压缩包。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "到 GitHub 的下载链路（codeload/github.com）不稳定或带宽差；大文件传输中途断开。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 换通道：改用 git clone（浅克隆 `git clone --depth 1 --branch v6.3.0 https://github.com/obra/superpowers.git`）或反向互换。",
          "2. 换网络：切换网络环境 / 开代理后重试；错峰重试。",
          "3. 离线解压：换任意可达的镜像站下载 zip → 手动解压 → 按「症状 6」接入；不依赖 zip 完整性的替代 = 只取 skills 目录（tarball 内含）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN Codex 桌面端安装（2026-06）——ZIP 下载超时 → git clone / 换网络 / 离线解压的社区解法。",
          "- 本市场实测：codeload tarball 544K 直连稳定（2.1s 完成，），证明该端点当前可用；探针 raw 端点超时，故手册统一避开 raw 路径。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-2",
        "agent": "https://trove.xin/agent/problems/superpowers-2"
      }
    },
    {
      "id": "superpowers-3",
      "title": "Plugin not found",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#3",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`/plugin install superpowers@...` 报 plugin not found / 找不到插件。你以为名字写错了，其实是**市场源没加**：superpowers 不在当前已配置的市场源里（上游市场与社区市场源名字不同）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "插件市场是\"先加源、后安装\"的机制：插件不在已配置的市场源里，就找不到。上游市场与社区市场源名字不同，教程里写哪个渠道的版本，就要加对应的源。",
          "",
          "插件安装是“先加源、后安装”的**两级寻址**：第一步把市场源注册进客户端（`/plugin marketplace add`），客户端拉取该源的插件目录；第二步 `install` 才在已注册的源里按名字查找。`plugin not found` 出现在第二步，说明第一步没生效——要么源没加，要么名字和源里的不对应。",
          "",
          "源名是“仓库名 + 渠道标识”（`obra/superpowers-marketplace`），不同渠道的同一插件是不同条目，教程用哪个渠道就得加哪个源。",
          "",
          "验证也走两级：`/plugin marketplace list` 看源有没有注册进去（配置层），再开新会话 `/find-skills` 看技能是否可见（运行时层）——只验证第二层，第一层出错时会反复踩同一个坑。",
          "",
          "| 市场源 | 用途 |",
          "|---|---|",
          "| obra/superpowers-marketplace | 社区渠道（superpowers 自己维护） |",
          "| claude-plugins-official | 上游官方渠道 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先加源、再安装、再验证（标准三步）",
          "/plugin marketplace add obra/superpowers-marketplace && /plugin install superpowers@superpowers-marketplace && /plugin marketplace list",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先加市场源 — **必做**",
          "",
          "```bash",
          "/plugin marketplace add obra/superpowers-marketplace",
          "```",
          "",
          "（或 `claude-plugins-official`，取决于你要哪个渠道的版本。）",
          "",
          "### 2. 再装 — **必做**",
          "",
          "```bash",
          "/plugin install superpowers@superpowers-marketplace",
          "```",
          "",
          "### 3. 确认与更新 — **必做**",
          "",
          "```bash",
          "/plugin marketplace list",
          "```",
          "",
          "检查源已生效；必要时 `/plugin marketplace update` 后重试。"
        ],
        "怎么确认修好了": [
          "",
          "新会话里验证技能可见（掘金教程标准验证路径）：",
          "",
          "```text",
          "/plugin marketplace list",
          "→ 源列表中含 superpowers 对应源",
          "新会话执行 /find-skills → 技能出现",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 源没加 | plugin not found，加源后解决（CSDN 案例） |",
          "| 渠道选错 | 官方/社区源名字不同，按教程来源选 |",
          "| 加了还找不到 | `/plugin marketplace update` 后重试 |",
          "| 未实测项 | Claude Code 插件市场路径本机不可测（无登录态），以上为网页方案整合 |",
          ""
        ],
        "症状": [
          "",
          "",
          "`/plugin install superpowers@...` 报 plugin not found / 找不到插件。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "市场源未添加或名字写错：superpowers 不在当前已配置的市场源里（上游市场与社区市场源名字不同）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 先加市场源：`/plugin marketplace add obra/superpowers-marketplace`（或 `claude-plugins-official`，取决于你要哪个渠道的版本）。",
          "2. 再装：`/plugin install superpowers@superpowers-marketplace`。",
          "3. 确认：`/plugin marketplace list` 检查源已生效；必要时 `/plugin marketplace update` 后重试。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN 问题排查指南（2026-01）——Plugin not found → 市场配置错误，加源后解决。",
          "- 掘金基础教程（2026）——标准三步：加源 → 安装 → 新会话 `/find-skills` 验证。",
          "- 未实测项（如实标注）：Claude Code 插件市场路径本机不可测（无登录态），以上为网页方案整合。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-3",
        "agent": "https://trove.xin/agent/problems/superpowers-3"
      }
    },
    {
      "id": "superpowers-4",
      "title": "安装后 hook error（Windows SessionStart hook fails）",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#4",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "安装后 Claude Code 启动时报 hook 错误（SessionStart hook 执行失败）。你以为是装坏了，重装一遍还是报。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "两个常见根因：",
          "",
          "| 根因 | 说明 |",
          "|---|---|",
          "| Windows 路径处理 bug | Claude Code 在 Windows 上的 SessionStart hook 路径处理问题（上游 Issue #420 属此场景） |",
          "| 版本不匹配 | 插件版本与 Claude Code 版本不匹配 || 版本不匹配 | 插件版本与 Claude Code 版本不匹配 |",
          "",
          "hook 不是插件自己跑的，是 **Claude Code 在会话启动时替插件执行**的：它读取插件配置里的 hook 声明，找到 `session-start.sh` 的路径，然后拉起这个脚本。所以 hook 报错有两种完全不同的病因：一是脚本本身有问题（语法/依赖缺失），二是 Claude Code 找不到/执行不了这个路径（Windows 路径处理 bug 属于这一类）。",
          "",
          "手动 `bash session-start.sh` 的意义就是**绕过启动器，直接验证脚本**：手动跑正常 = 启动器路径问题（升级或换形态）；手动跑也报错 = 脚本本身问题（修脚本）。一步就把两类病因分开。",
          "",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先升级两边版本，再手动跑 hook 看真实报错",
          "claude update && /plugin update && bash <插件目录>/session-start.sh",
          "```",
          "",
          "升级后重试；仍报错就用手动执行结果定位。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 升级 Claude Code 与插件版本后重试 — **必做**",
          "",
          "```bash",
          "claude update",
          "```",
          "",
          "然后 `/plugin update` 更新插件，重开会话。",
          "",
          "### 2. 手动执行对应 hook 脚本 — **必做**",
          "",
          "```bash",
          "bash <插件目录>/session-start.sh",
          "```",
          "",
          "看真实报错（CSDN 案例路径：手动跑 session-start.sh 定位）。",
          "",
          "### 3. Windows 场景改走纯技能形态 — **必做**",
          "",
          "不依赖 hook 的路径（见症状 6 条目）：把 `skills/` 复制到技能目录，当技能用。"
        ],
        "怎么确认修好了": [
          "",
          "启动不再报 hook 错误：",
          "",
          "```text",
          "Claude Code 新会话启动 → 无 SessionStart hook 错误",
          "```",
          "",
          "或手动执行 hook 脚本输出正常（无报错）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Windows | 上游 Issue #420 路径处理 bug，升级或改纯技能形态 |",
          "| 版本不匹配 | 升级 Claude Code 与插件版本 |",
          "| 手动定位 | `bash session-start.sh` 看真实报错 |",
          "| macOS/Linux | 该 bug 主要出现在 Windows 场景 |",
          ""
        ],
        "症状": [
          "",
          "",
          "安装后 Claude Code 启动报 hook 错误（SessionStart hook 执行失败）。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "Claude Code 在 Windows 上的路径处理 bug（上游 Issue #420 属此场景）；或插件版本与 Claude Code 版本不匹配。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 升级 Claude Code 与插件版本后重试。",
          "2. 手动执行对应 hook 脚本（session-start.sh）看真实报错。",
          "3. Windows 场景可改走纯技能形态（不依赖 hook 的路径，见「症状 6」）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（2026-01）——Plugin hook error → 更新版本或手动跑 session-start.sh。",
          "- 国外信号（L32 窗口期匿名摘录）：上游 Issue #420 为 Windows SessionStart hook 路径处理 bug。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-4",
        "agent": "https://trove.xin/agent/problems/superpowers-4"
      }
    },
    {
      "id": "superpowers-5",
      "title": "装完看不到技能",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "agent-runtime",
      "category_label": "Agent 工具·运行与配置",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#5",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "安装成功，但 `/find-skills`（或等价技能查看命令）看不到 brainstorming / systematic-debugging / test-driven-development 等技能。装完了却用不上。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "| 原因 | 表现 |",
          "|---|---|",
          "| 技能缓存未刷新 | 装完立即查，缓存还是旧的 |",
          "| 未完全退出重开新会话 | 技能在启动时扫描，运行中不刷新 |",
          "| 路径放错 | 技能被放到 Claude 读不到的目录 |",
          "| 缓存不全 | 上游 Issue #818：14 个技能只装了 7 个，重装补齐 |",
          "",
          "“安装成功”和“技能可见”之间隔着**缓存**这道桥：插件/技能包安装时先把内容解包落盘，客户端把扫描结果（技能清单）缓存起来供会话内查询——安装动作和扫描动作是两个时间点。装完立刻查，查的是旧缓存（还没重新扫描）；运行中的会话持有的是启动时的内存态，不会自动刷新。",
          "",
          "这就是“装完看不见”的三个确定性格子：**缓存未刷新**（重开新会话触发重新扫描）、**扫描目录不对**（技能没落在 `~/.claude/skills/` 等固定路径，扫描根本看不到）、**缓存不全**（上游下载/解包中断导致技能数少了，如 Issue #818 的 7/14）。",
          "",
          "验证顺序固定：先 `/plugin list` 确认插件本身可用（排掉插件层问题）→ 完全退出重开（强制重新扫描）→ 再 `/find-skills`。还有个提前量：tarball 解包后先数技能目录/SKILL.md/frontmatter，完整性在安装前就能确认。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认插件状态 → 完全退出重开 → 再查",
          "/plugin list && /exit",
          "```",
          "",
          "重开新会话后 `/find-skills`；路径按第 3 步核对。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认插件状态 — **必做**",
          "",
          "```bash",
          "/plugin list",
          "```",
          "",
          "感叹号 = 不可用（见症状 6 条目）。",
          "",
          "### 2. 完全退出后重开新会话 — **必做**",
          "",
          "Ctrl+C 或 `/exit` 完全退出（不是开新窗口），重开再查。",
          "",
          "### 3. 检查路径 — **排查**",
          "",
          "Claude Code 读 `~/.claude/skills/`（或插件安装目录），不要放在其他目录：",
          "",
          "```bash",
          "ls ~/.claude/skills/",
          "```",
          "",
          "### 4. 缓存不全时重跑安装命令 — **必做**",
          "",
          "上游 Issue #818 场景：14 个技能只装了 7 个，重装补齐。"
        ],
        "怎么确认修好了": [
          "",
          "技能完整可见（实测校验形态）：",
          "",
          "```text",
          "tarball 结构：14 技能目录 / 14 SKILL.md / frontmatter 14/14 有效",
          "新会话 /find-skills → brainstorming / systematic-debugging / test-driven-development 等出现",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 缓存未刷新 | 重开新会话（上游 Issue #818 重装补齐） |",
          "| 路径放错 | 放 `~/.claude/skills/`（或插件安装目录） |",
          "| 插件感叹号 | 见症状 6 条目的本地/离线形态处理 |",
          "| 完整性预检 | tarball 解包后可先数技能目录/SKILL.md/frontmatter |",
          ""
        ],
        "症状": [
          "",
          "",
          "安装成功，但 `/find-skills`（或等价技能查看命令）看不到 brainstorming / systematic-debugging / test-driven-development 等技能。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "技能缓存未刷新；未完全退出重开新会话；技能被放到 Claude 读不到的目录（路径放错）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. `/plugin list` 确认插件状态（感叹号 = 不可用，见症状 6）。",
          "2. 完全退出（Ctrl+C 或 /exit）后重开新会话再查。",
          "3. 检查路径：Claude Code 读 `~/.claude/skills/`（或插件安装目录），不要放在其他目录。",
          "4. 缓存不全时重跑安装命令（上游 Issue #818 场景：14 个技能只装了 7 个，重装补齐）。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN（2026-06）+ 掘金 FAQ（2026）——退出重启 + 路径检查为社区通用解法。",
          "- 国外信号（L32 匿名摘录）：上游 Issue #818 技能缓存不完整（7/14）。",
          "- 本市场实测：tarball 结构 14 技能目录 / 14 SKILL.md / frontmatter 14/14 有效——技能完整性可提前校验。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-5",
        "agent": "https://trove.xin/agent/problems/superpowers-5"
      }
    },
    {
      "id": "superpowers-6",
      "title": "本地/离线部署后插件显示不可用（感叹号）",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#6",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "手动下载 zip 解压放到 `~/.claude/plugins/local-superpowers` 后，`/plugin list` 显示感叹号不可用；直接复制仓库目录也报缺文件。离线装不动。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "tarball 内各 harness 目录（`.claude-plugin`/`.codex-plugin`/`.cursor-plugin`）带 plugin.json，但**不含编译产物 dist/index.js**——本地插件形态要求完整插件包；纯 SKILL 仓库按插件路径接入会缺件（CSDN 案例：缺 plugin.json + dist/index.js 导致不可用）。",
          "",
          "同一个仓库可以有两种**接入形态**，它们的加载机制完全不同：**技能形态**是“目录扫描”——客户端在启动时扫描 skills 目录，每个含 SKILL.md 的子目录就是一个技能，纯文件即可，不需要任何编译产物；**插件形态**是“代码加载”——客户端读取 plugin.json，加载并执行 dist/index.js 里的插件代码，这是运行时代码，缺了它插件就加载失败（感叹号）。",
          "",
          "问题出在“用插件形态去接一个技能形态的仓库”：tarball 里的 harness 目录带 plugin.json（声明自己是插件），但不带编译产物（作者只发布了技能形态），两头对不上就报缺文件。",
          "",
          "这也给出了判断路径：**先想清楚你要哪种形态，再按对应形态的完整要求接入**——要技能就复制 `skills/` 目录（推荐，实测 14/14）；非要插件形态就要走 marketplace 流程拿到完整包（含 dist/）。",
          "",
          "| 接入形态 | 要求 | 是否可行 |",
          "|---|---|---|",
          "| 当技能用（推荐） | 复制 `skills/` 到技能目录 | ✅ 实测 14/14 |",
          "| 当本地插件用 | 完整插件包（含 dist/） | ⚠️ tarball 无 dist/，需 marketplace 流程 |",
          "| Codex CLI 形态 | `cp -r skills ~/.codex/skills/superpowers` | ✅ 本市场实测 |",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：当技能用（推荐）——复制 skills 目录到 Claude 能读的位置，新会话启用",
          "cp -r <仓库>/skills ~/.claude/skills/superpowers",
          "```",
          "",
          "Codex CLI 形态同理：`cp -r skills ~/.codex/skills/superpowers`（本市场实测路径，无需编译产物）。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 当技能用（推荐） — **必做**",
          "",
          "```bash",
          "cp -r <仓库>/skills ~/.claude/skills/superpowers",
          "```",
          "",
          "新会话启用；或 `npx superpowers-zh` 方式拉取。",
          "",
          "### 2. 当本地插件用 — **必做**",
          "",
          "```bash",
          "/plugin marketplace add <本地路径>/.claude/plugins/superpowers/superpowers-main",
          "```",
          "",
          "然后 `/plugin install superpowers@superpowers-dev` → `/reload-plugins`。",
          "",
          "### 3. Codex CLI 形态（本市场实测路径） — **必做**",
          "",
          "```bash",
          "cp -r skills ~/.codex/skills/superpowers",
          "```",
          "",
          "无需编译产物，纯文件形态。"
        ],
        "怎么确认修好了": [
          "",
          "技能目录结构与有效性（实测形态）：",
          "",
          "```text",
          "ls ~/.codex/skills/superpowers",
          "→ 14 技能目录 / 14 SKILL.md / frontmatter 14/14 有效",
          "```",
          "",
          "`/plugin list` 不再显示感叹号（本地插件形态），或 `/find-skills` 可见技能（技能形态）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| tarball 无 dist/ | 本地插件形态缺件，当技能用或走 marketplace 流程（解包核实） |",
          "| Codex CLI | `cp -r skills ~/.codex/skills/superpowers`（实测） |",
          "| Claude Code | 技能目录复制或 marketplace 流程 |",
          "| 感叹号 | 按形态重接：技能用复制，插件用 marketplace |",
          ""
        ],
        "症状": [
          "",
          "",
          "手动下载 zip 解压放到 `~/.claude/plugins/local-superpowers` 后，`/plugin list` 显示感叹号不可用；直接复制仓库目录也报缺文件。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "tarball 内各 harness 目录（.claude-plugin/.codex-plugin/.cursor-plugin）带 plugin.json，但不含编译产物 dist/index.js——本地插件形态要求完整插件包；纯 SKILL 仓库按插件路径接入会缺件。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 当技能用（推荐）：把仓库内 `skills/` 下全部技能目录复制到 Claude 能读的 skills 目录，新会话启用；或 `npx superpowers-zh` 方式拉取。",
          "2. 当本地插件用：`/plugin marketplace add <本地路径>/.claude/plugins/superpowers/superpowers-main` → `/plugin install superpowers@superpowers-dev` → `/reload-plugins`。",
          "3. Codex CLI 形态（本市场实测路径）：下载 tarball → `cp -r skills ~/.codex/skills/superpowers`，无需编译产物。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：[CSDN m0_58528895/161265224](https://blog.csdn.net/m0_58528895/article/details/161265224)（2026-05）——离线部署缺 plugin.json + dist/index.js 导致不可用（作者原仓库确实无编译产物）。",
          "- 网页案例：CSDN aicoding.csdn.net（2026）——本地路径 marketplace add → install → reload 的可行接入流程。",
          "- 本市场实测：tarball 含 .claude-plugin/plugin.json 但无 dist/（2026-08-28 解包核实）；skills 目录复制形态 14/14 有效。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-6",
        "agent": "https://trove.xin/agent/problems/superpowers-6"
      }
    },
    {
      "id": "superpowers-7",
      "title": "无本地文件读取权限 / 渠道不一致",
      "domain": "skills",
      "domain_label": "技能管理",
      "category": "permission-security",
      "category_label": "权限与安全",
      "applies_to": [
        "superpowers"
      ],
      "evidence": "site docs#7;local repro log",
      "source": "本站实测沉淀（2026-08-28）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "Codex 桌面端装完读不到技能（无本地文件读取权限）；CodeBuddy 等产品装 OpenSpec/Superpowers 报渠道不一致错误。同一个包，换个产品就装不上。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "- 桌面端**未授予本地文件读取权限**时，技能目录读不到；",
          "- 不同产品对「插件/技能」的**格式与目录约定不同**：Codex 桌面端、CodeBuddy、Claude Code 各一套，混用就报渠道不一致。",
          "",
          "| 产品 | 形态 |",
          "|---|---|",
          "| Codex | skills 目录（纯文件，本市场实测） |",
          "| Claude Code | 插件市场或 skills 目录 |",
          "| CodeBuddy | 自身渠道（先查该产品支持 Superpowers 的形态） |",
          "",
          "“插件/技能”在不同产品里不是同一个东西：每个产品自己定义“技能长什么样、放在哪个目录、怎么被扫描”。Codex 桌面端是纯文件 skills 目录（启动时扫描目录，每个含 SKILL.md 的子目录一个技能）；Claude Code 是插件市场或 skills 目录；CodeBuddy 有自己的渠道。",
          "",
          "把 A 产品的目录搬到 B 产品，等于拿 B 的扫描器去读 A 的布局——读不到就报“缺文件/渠道不一致”。",
          "",
          "桌面端还有一层**系统权限**：桌面应用读用户目录之外的位置需要用户显式授予权限，没授权时“目录存在但读不到”。所以接入任何产品都固定两步：**先查该产品认什么形态**（文档/官方说明），**再按它的目录约定放置**（并处理桌面端权限）。判断“为什么读不到”也分层：目录位置对不对（约定层）→ 权限给没给（系统层）→ 格式对不对（内容层）。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：Codex 桌面端——先在设置里授予本地文件读取权限，再按产品形态安装",
          "# 设置 → 权限/安全 → 允许本地文件读取；装法：cp -r skills ~/.codex/skills/superpowers",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 在对应产品的设置里授予本地文件读取权限 — **必做**",
          "",
          "Codex 桌面端：设置里开本地文件读取权限（CSDN 2026-06 案例）。",
          "",
          "### 2. 按产品形态安装 — **必做**",
          "",
          "```bash",
          "cp -r skills ~/.codex/skills/superpowers",
          "```",
          "",
          "Codex 用 skills 目录；Claude Code 用插件市场或 skills 目录；CodeBuddy 用其自身渠道。",
          "",
          "### 3. 渠道不一致报错时以产品自带渠道为准 — **必做**",
          "",
          "不要混用安装路径；以产品自带插件安装渠道为准。"
        ],
        "怎么确认修好了": [
          "",
          "Codex 形态安装与卸载闭环（本市场实测输出）：",
          "",
          "```text",
          "[install] superpowers installed: OK（cp -r skills ~/.codex/skills/superpowers）",
          "[uninstall] superpowers removed: OK（rm -rf，零残留）",
          "```",
          "",
          "新会话技能可见；CodeBuddy/Claude Code 各按自身渠道验证。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Codex 桌面端 | 先授本地文件读取权限，再 skills 目录安装 |",
          "| CodeBuddy | 用自身渠道，先查支持形态（CSDN 2026-07） |",
          "| Claude Code | 插件市场或 skills 目录 |",
          "| 渠道混用 | 报错时回产品自带渠道 |",
          "| 未实测项 | Claude Code 形态（无登录态）；Codex 形态纯文件无权限/端口/后台需求 |",
          ""
        ],
        "症状": [
          "",
          "",
          "Codex 桌面端装完读不到技能（无本地文件读取权限）；CodeBuddy 等产品装 OpenSpec/Superpowers 渠道不一致报错。",
          "",
          ""
        ],
        "原因": [
          "",
          "",
          "桌面端未授予本地文件读取权限；不同产品对「插件/技能」的格式与目录约定不同（Codex 桌面端、CodeBuddy、Claude Code 各一套）。",
          "",
          ""
        ],
        "解法": [
          "",
          "",
          "1. 在对应产品的设置里授予本地文件读取权限。",
          "2. 按产品形态安装：Codex 用 skills 目录；Claude Code 用插件市场或 skills 目录；CodeBuddy 用其自身渠道（先查该产品支持 Superpowers 的形态）。",
          "3. 渠道不一致报错时，以产品自带插件安装渠道为准，不要混用。",
          "",
          ""
        ],
        "验证证据": [
          "",
          "",
          "- 网页案例：CSDN Codex 桌面端（2026-06）——权限问题；CSDN CodeBuddy（2026-07）——渠道不一致。",
          "- 本市场形态边界：Codex 形态纯文件（无权限/端口/后台需求）；Claude Code 形态未实测（无登录态）。",
          ""
        ],
        "本市场实测安装 / 卸载（2026-08-27/28）": [
          "",
          "安装（站内安装说明 superpowers，隔离 HOME 复现）：",
          "",
          "```",
          "[install][WARN] 注意：superpowers 与以下条目存在未验证组合（可能冲突）: ruflo",
          "[probe][PASS] github：GitHub 源码下载 / GitHub 网页 / GitHub API 可用",
          "[install] 执行: curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0 && ... && cp -r .../skills ~/.codex/skills/superpowers",
          "superpowers installed: OK",
          "[install] 已记录本地安装回执（匿名；~/.trove/receipts/）",
          "[install][PASS] superpowers 安装完成",
          "```",
          "",
          "卸载（站内卸载说明 superpowers，green 档一键执行）：",
          "",
          "```",
          "[uninstall] 执行: rm -rf ~/.codex/skills/superpowers",
          "[uninstall] 验证: test ! -d ~/.codex/skills/superpowers",
          "superpowers removed: OK",
          "[uninstall][PASS] superpowers 卸载完成，零残留",
          "```",
          "",
          "完整日志：实测记录（本地留存）；连通性：实测记录（本地留存） + 站内文档。",
          ""
        ],
        "冲突标注": [
          "",
          "- possible-compatible：oh-my-claudecode / agent-governance-toolkit / deepcode（无共享写面，共装判定见冲突矩阵）",
          "- possible-conflict：ruflo（Claude 形态共享 `~/.claude/skills` 写面，未实测）——同机装 ruflo 时建议用 Codex 形态",
          "- 形态维度：Codex 形态只写 `~/.codex/skills/superpowers`，与其他条目共享面最小；Claude 插件市场形态写面在 Claude 插件目录，按形态分别评估",
          ""
        ],
        "名称混淆": [
          "",
          "无同名高危条目（检索时注意 superpower 单复数拼写差异）。",
          ""
        ],
        "判据（验证状态）": [
          "",
          "- 症状清单：",
          "  - [x] /plugin install 报 fatal: Failed to connect to github.com port 443（CSDN，2026-05；解包核实 + 探针证据）",
          "  - [x] ZIP 下载超时 → git clone/换网络/离线解压（CSDN，2026-06）",
          "  - [x] Plugin not found → 市场源配置错误（CSDN，2026-01；掘金教程整合）",
          "  - [x] Plugin hook error → 更新版本或手动跑 session-start.sh（CSDN，2026-01；上游 Issue #420）",
          "  - [x] 装完看不到技能 → 完全退出重启 + 路径检查（CSDN，2026-06；掘金 FAQ；上游 Issue #818）",
          "  - [x] 无本地文件读取权限 / 渠道不一致（CSDN，2026-06/07）",
          "  - [x] 本地插件缺 dist 产物显示不可用（CSDN 2026-05 + aicoding 案例；tarball 解包核实无 dist/）",
          "- 实测步骤：",
          "本站实测记录",
          "  - [x] 卸载验证（green 档一键卸载 → verify 零残留）",
          "  - [x] 网络/镜像场景（network-probe full：codeload 1.91s / gh-proxy 7.8s / raw 超时）",
          "  - [x] 未能实测项：Claude Code 插件市场场景本机不可测（无登录态）——网页方案整合，如实标注",
          ""
        ],
        "验收": [
          "",
          "- [x] 症状 → 原因 → 解法 → 验证证据 四段齐全（7 症状）",
          "- [x] 名称混淆已点名：无同名高危条目",
          "- [x] check-words HARD 0",
          "- [x] validate 通过",
          "- [x] 未碰 GitHub / 未用任何账号"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/superpowers-7",
        "agent": "https://trove.xin/agent/problems/superpowers-7"
      }
    },
    {
      "id": "tar-strip-components",
      "title": "解压 tar 包多出顶层目录 / 文件散落一地 —— --strip-components 用法（macOS 报 Option not supported）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "tar"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://stackoverflow.com/questions/60486284/option-not-supported-in-mac-terminal",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "解压 tar.gz 后目录结构与预期不符：直接 `tar -xzf pkg.tar.gz` 多出 `pkg/` 前缀；macOS 上想用 `--strip-components` 去掉却报 `Option --strip-components is not supported`。开源包普遍带一个顶层目录，直接解压会多一层或把文件散到错误位置。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "tar 包内成员的路径包含打包时的顶层目录前缀，`tar -xzf` 按原样还原路径，不会\"聪明地\"去掉外层目录；`--strip-components=N` 就是干这个的：解压时剥离路径前 N 个组件。两个常见混淆点：",
          "",
          "| 混淆点 | 说明 |",
          "|---|---|",
          "| GNU tar vs BSD tar | GNU tar（Linux/WSL 自带）支持 `--strip-components`；BSD tar（macOS 自带）不支持，只支持 `-s` 替换表达式 |",
          "| strip 数量取错 | N 大于实际路径层级时，成员会被**静默跳过**（不报错、文件也不出来），比\"多一层\"更难发现 |",
          "",
          "tar 包内的成员路径是**打包时记录的完整相对路径**，解压按原样还原，不会自动去掉顶层目录——`--strip-components=N` 是显式指令：解压时剥离前 N 个路径组件。",
          "",
          "两个混淆点：GNU tar（Linux/WSL 自带）支持 `--strip-components`，BSD tar（macOS 自带）不支持（用 `-s` 替换表达式）；N 取大会导致成员**静默跳过**（不报错、文件也不出来）——比「多一层目录」更难发现，因为看起来像是成功解压。",
          "",
          "确定性推论：解压后发现目录层级不对 = 用 `tar -tf <包> | head` 看真实成员路径，数清楚要剥几层再定 N；macOS 上报「unknown option」= BSD tar，改用 `-s` 或装 GNU tar；「解压成功但文件缺失」= N 过大，减小 N；判断路径：先 `-tf` 看清路径结构，再定 N，不要猜。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：先看包内结构，再剥离一层顶层目录（GNU tar / Linux / WSL）",
          "tar -tzf pkg.tar.gz | head && mkdir -p out && tar -xzf pkg.tar.gz -C out --strip-components=1",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先看包内结构再解压 — **必做**",
          "",
          "```bash",
          "tar -tzf pkg.tar.gz",
          "```",
          "",
          "数清楚顶层有哪几层目录。",
          "",
          "### 2. 去掉一层顶层目录 — **必做**",
          "",
          "```bash",
          "mkdir -p out",
          "tar -xzf pkg.tar.gz -C out --strip-components=1",
          "```",
          "",
          "指定目标目录，避免散到当前目录。",
          "",
          "### 3. macOS（BSD tar）替代 — **必做**",
          "",
          "```bash",
          "brew install gnu-tar",
          "gtar -xzf pkg.tar.gz --strip-components=1",
          "```",
          "",
          "或先 `tar -xzf` 到临时目录再 `mv pkg/* .`（简单场景够用）。",
          "",
          "### 4. 剥离层数拿不准时 — **必做**",
          "",
          "先 `tar -tzf` 看路径，再决定 N；strip 后立刻 `find`/`ls` 确认文件真的出来了——**剥离过度不会报错，只有\"文件不见了\"这个结果**。",
          "",
          "### 5. 脚本化时 — **必做**",
          "",
          "解压步骤固定为 `tar -xzf <pkg> -C <dir> --strip-components=1` 后**校验关键文件存在**，而不是只检查退出码。"
        ],
        "怎么确认修好了": [
          "",
          "解压位置正确（实测输出形态）：",
          "",
          "```text",
          "--strip-components=1 解压 → out2/a.txt、out2/b.txt（前缀被剥离，实测）",
          "反例：--strip-components=2（大于层级）→ 退出码 0 但一个文件都没解出来（实测确认）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| GNU tar（Linux/WSL） | 支持 --strip-components（实测 tar 1.35） |",
          "| macOS BSD tar | 报 Option not supported（[SO #60486284](https://stackoverflow.com/q/60486284)）；装 gnu-tar 或 mv 替代 |",
          "| 剥离过度 | 静默跳过，不报错（实测） |",
          "| 目标目录 | -C 指定，避免散落 |",
          "| 脚本 | 校验关键文件存在，别只看退出码 |",
          ""
        ],
        "症状": [
          "",
          "解压 tar.gz 后目录结构与预期不符：",
          "",
          "```",
          "# 预期把文件解到当前目录，结果多了一层包目录",
          "$ tar -xzf pkg.tar.gz",
          "$ find . -type f",
          "./pkg/a.txt",
          "./pkg/b.txt        # 多出 pkg/ 前缀",
          "",
          "# macOS 上想用 --strip-components 去掉前缀，却报错",
          "$ tar -xzf pkg.tar.gz --strip-components=1",
          "tar: Option --strip-components is not supported",
          "```",
          "",
          "特征：下载的开源包/二进制包普遍打包时带一个顶层目录（`pkg/`），直接解压会多一层或把文件散到错误位置；",
          "Linux 的 GNU tar 支持 `--strip-components`，但 macOS 自带的是 BSD tar，不支持该选项，报 `Option --strip-components is not supported`。",
          ""
        ],
        "原因": [
          "",
          "tar 包内成员的路径包含打包时的顶层目录前缀。`tar -xzf` 按原样还原路径，不会\"聪明地\"去掉外层目录；",
          "`--strip-components=N` 就是干这个的：解压时剥离路径前 N 个组件（目录）。",
          "两个常见混淆点：",
          "- GNU tar（Linux/WSL 自带）支持 `--strip-components`；BSD tar（macOS 自带）不支持，只支持 `-s` 替换表达式或根本没有对应选项；",
          "- `strip` 数量取错：N 大于实际路径层级时，成员会被静默跳过（不报错、文件也不出来），比\"多一层\"更难发现。",
          ""
        ],
        "解法": [
          "",
          "1. **先看包内结构再解压**：`tar -tzf pkg.tar.gz`（或 `tar -tzf pkg.tar.gz | head`），数清楚顶层有哪几层目录。",
          "2. **去掉一层顶层目录**：`tar -xzf pkg.tar.gz --strip-components=1`（GNU tar / Linux / WSL）；",
          "   解压前先 `mkdir -p out` 并用 `-C out` 指定目标目录，避免散到当前目录。",
          "3. **macOS（BSD tar）替代**：",
          "   - 装 GNU tar：`brew install gnu-tar`，用 `gtar -xzf pkg.tar.gz --strip-components=1`；",
          "   - 或先 `tar -xzf` 到临时目录再 `mv pkg/* .`（简单场景够用）。",
          "4. **剥离层数拿不准时**：先 `tar -tzf` 看路径，再决定 N；strip 后立刻 `find`/`ls` 确认文件真的出来了——",
          "   剥离过度不会报错，只有\"文件不见了\"这个结果。",
          "5. **脚本化时**：解压步骤固定为 `tar -xzf <pkg> -C <dir> --strip-components=1` 后校验关键文件存在，而不是只检查退出码。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，GNU tar 1.35，纯本地）：",
          "  - 构造 `pkg/{a.txt,b.txt}` 打包 `pkg.tar.gz`（含顶层目录 pkg/）；",
          "  - 直接解压 → `out1/pkg/a.txt`、`out1/pkg/b.txt`（多一层，与\"多出顶层目录\"症状一致）；",
          "  - `--strip-components=1` 解压 → `out2/a.txt`、`out2/b.txt`（前缀被剥离）；",
          "  - 反例：`--strip-components=2`（大于层级）→ 退出码 0 但一个文件都没解出来（静默跳过，实测确认）；",
          "  - 工具版本：`tar (GNU tar) 1.35`。",
          "- macOS BSD tar 的 `Option --strip-components is not supported` 报错来自素材（[SO #60486284](https://stackoverflow.com/q/60486284)），本环境（Linux）无法实测 BSD tar，仅记录现象与官方替代方案。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/tar-strip-components",
        "agent": "https://trove.xin/agent/problems/tar-strip-components"
      }
    },
    {
      "id": "tech-debt-consensus",
      "title": "技术债越欠越多，接手\"屎山\"代码到底该不该重写？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "我交过一笔最贵的\"学费\"，是一次全量重写。",
          "",
          "当时我接手一个跑了六年的老系统，业务逻辑缠成一团，改一个字段要顺藤摸瓜找出七八处关联。我看了三天，越看越气：\"这种代码留着干嘛？重写！\"我拉上两个同事，用当时最流行的新框架，从零开始搭。三个月后，新系统能跑了——然后我们傻眼了：老系统六年的边界情况、暗坑、兼容逻辑，全藏在那些\"烂代码\"里，新系统一个都没带过来。上线第一个月，业务方报了一百多个\"以前能用现在不能用了\"。",
          "",
          "最后我们又花了大半年，把老系统的逻辑一点一点补回去。",
          "",
          "那一次重写，成本是新写的三倍，还差点把业务线干没。",
          "",
          "这不是我一个人的故事。我后来见过太多团队掉进同一个坑：接手老系统，越看越不顺眼，全员高呼\"重写\"，立项、排期、干三个月，然后被现实打脸。也有反过来的——一个朋友的公司把老系统当\"祖传宝贝\"供着，什么都不敢动，结果每次需求都要绕路，半年后系统终于被业务抛弃，重写成本翻了十倍。",
          "",
          "如果你也在经历这些——手头的代码越改越乱，天天在\"屎山\"上贴瓷砖；或者你刚接手一个老项目，满脑子都是\"重写吧\"——这篇文章是写给同类人的。它不是重构教程，是那些在真实项目里还过技术债、也踩过重写大坑的人，用事故换来的共识：**技术债不是靠一次重写还清的，是靠一次次小步偿还的。**",
          ""
        ],
        "原因": [
          "",
          "先搞清楚\"技术债\"到底是什么。这个词是 Ward Cunningham（wiki 之父）1992 年发明的：**借债本身不是问题，问题是不还利息**。",
          "",
          "把非理想的代码、设计、架构决策看作\"本金\"，把维护、理解、扩展、修复它们多花的时间看作\"利息\"——这跟金融债务一模一样：借的时候很爽（快），利息每天都在涨（越来越难改），而且你永远不知道哪一天会暴雷（改一行崩全局）。",
          "",
          "技术债是怎么欠下的？不是开发者偷懒。我复盘过自己和身边团队踩过的坑，债的来源几乎都是\"当时看起来合理的决定\"：",
          "",
          "1. **赶工**：\"这版先这么写，上线后重构\"——上线后没人记得，记得了也没人敢动；",
          "2. **需求变了**：当初按 A 场景设计，业务变成了 B 场景，代码在原架构上打补丁，越打越厚；",
          "3. **换人**：写代码的人离职了，注释和代码各说各话，后来的人只能猜；",
          "4. **测试敷衍**：没有自动化测试，改代码如履薄冰，于是干脆不改，债越滚越大；",
          "5. **技术过时**：依赖库停留在多年前的版本，想升级发现没人知道为什么当初这么选。",
          "",
          "这里有个特别扎心的机制：**技术债的利息是复利，而且收利息的时机你控制不了。** 前六个月你可能毫无感觉，甚至觉得\"代码还行\"；等系统长到一定规模，每一次改动都开始牵一发动全身，你每天 50% 的时间在修 bug 而不是做功能——这时候债已经大到\"不敢还\"了：重构的 ROI 在老板眼里是零，因为\"没坏就别修\"，可系统一旦崩了，老板又觉得是你们没维护好。",
          "",
          "我观察到的真相是：**还债的最大障碍从来不是技术，是\"一次还清\"的执念。** 全量重写是最贵、最危险、成功率最低的还债方式；真正有效的方式，是绞杀者模式——像绞杀榕一样，在新系统旁边一点点长出来，把老系统慢慢包住、替换掉。下面每一条经验，都是这个共识在不同场景下的展开。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：别重写，绞杀它——小步还债，每步可回滚",
          "",
          "技术债只能分批还，不能一次还清。**凡是\"推倒重来\"的还债方案，成功率都低得可怜；凡是\"新老并存、逐步替换\"的方案，虽然慢，但稳。**赶时间只看这一条：接手\"屎山\"先别骂，先给系统装上测试保护网，然后用绞杀者模式一小块一小块地替换。下面的经验 1~5 是为什么。",
          "",
          "### 经验 1：识别先行——把\"感觉烂\"变成\"清单\"",
          "",
          "现象：几乎所有团队都知道自己有技术债，但问\"债在哪\"，没人说得清。我见过最典型的状态：\"这个系统很烂\"是共识，\"烂在哪、先还哪\"是空白。结果就是：想还债的人无从下手，不想还债的人有理由拖延。",
          "",
          "做法：先把债显性化。三件套：①画一张系统热力图（SonarQube 等技术债扫描工具，按模块标出复杂度、重复代码、坏味道）；②建一张技术债清单，每条写清\"在哪、为什么是债、不还的后果\"；③用四象限排序——高影响高紧急的优先，低影响低紧急的排队。核心逻辑只有一条：**把\"感觉\"变成数字和清单，债才谈得上\"还\"。**",
          "",
          "为什么：技术债最可怕的地方是隐形。它不报错、不告警，只是让你的每次改动都越来越慢。清单和热力图把隐形的东西显性化，还债才有优先级、有进度、有交代——老板能看到 ROI，团队能看到盼头。",
          "",
          "### 经验 2：别全量重写——绞杀者模式是唯一被反复验证的路线",
          "",
          "现象：这是翻车率最高的一步，我自己就翻过（开头那个三个月重写就是）。重写的诱惑太大了：新框架、新架构、干净的代码，谁不想要？但现实是：老系统六年的边界情况、兼容逻辑、暗坑，全部藏在\"烂代码\"里，重写时你根本不知道它们存在，直到上线后业务方一个个找上门。行业里著名的重写翻车案例（Netscape 重写、Healthcare.gov 初期）都是同一个剧本。",
          "",
          "做法：用绞杀者模式（Strangler Fig Pattern，Martin Fowler 提出）：新功能用新架构写，老功能一个模块一个模块地迁移到新系统，每迁移一块就下线一块老模块，直到老系统被完全\"绞杀\"。关键原则：**新老系统通过防腐层（Adapter）对接，老系统一天没死，就一天是事实源。** 迁移顺序按\"业务价值 × 风险\"排：先迁独立、低风险、高频的模块，建立信心后再碰核心。",
          "",
          "为什么：绞杀者模式把\"一次大爆炸\"拆成\"无数次小爆炸\"，每次爆炸范围可控、可回滚、可验证。业务不中断，老板不恐慌，团队有正反馈。全量重写的本质是\"用未来三年的风险赌现在三个月的爽\"，而绞杀者模式是\"用每个迭代的小步，换不翻车的确定性\"。",
          "",
          "### 经验 3：测试保护网——没测试，别碰核心逻辑",
          "",
          "现象：重构最大的恐惧是\"改坏了怎么办\"。没有测试的系统，改一行代码要靠\"感觉\"和\"运气\"；改完上线，出了问题连是哪次改动引入的都查不出来。我见过太多团队死在\"先重构后补测试\"的顺序上——重构到一半发现行为变了，又不知道是不是自己改坏的，只能回滚，来回几次，士气就没了。",
          "",
          "做法：**先补测试，再重构。** 不用追求全覆盖，先把\"关键路径\"包住：核心业务逻辑、资金/权限/数据一致性相关的高危代码，用测试固定住当前行为（哪怕当前行为是错的，也要先固定，因为那是线上行为）。实测有效的参考线：核心代码覆盖率 80%+ 再动，至少把高危代码的关键路径包住。重构时每步跑测试，红→绿→重构循环。",
          "",
          "为什么：测试保护网是\"敢动\"的前提。有了它，重构从\"赌博\"变成\"验证\"——改坏了测试会告诉你，而不是上线后用户告诉你。这也是绞杀者模式能跑起来的地基：没有测试，你连\"这块迁过去行为没变\"都无法证明。",
          "",
          "### 经验 4：小步重构——单次改动小到可以随时回滚",
          "",
          "现象：重构翻车的第二个常见原因是\"步子太大\"。我见过同事一次重构改了 2000 行，改完编译都过不去，回滚又舍不得，最后熬夜修到上线前夜。经验规律：**重构的难度和改动量不是线性关系，是指数关系**——每多一个逻辑分支，理解成本就组合式上涨。",
          "",
          "做法：单次重构控制在可回滚的粒度（行业共识：一次不超过 50 行，最多不碰超过 500 行的 MR/PR）。每次重构前先提交一个干净的基线，改完立刻跑测试 + 让同事 review；CI 全绿再提交下一步。别信\"这次改动就是这么多，没法拆\"——拆不动说明设计抽象不够，先做架构调整再动手。",
          "",
          "为什么：小步重构把\"改坏了\"的成本从\"通宵回滚\"降到\"撤销一个 commit\"。而且小步重构有个隐藏收益：每一步都有同事 review，知识在团队里流动——重构一次，全组都更懂这块代码了，这是大重构永远给不了的。",
          "",
          "### 经验 5：把还债排进迭代——债是还的，不是等的",
          "",
          "现象：技术债清单建了，优先级排了，然后呢？最常见的结局是清单吃灰——因为\"还债\"没有排进迭代，永远被新需求挤掉。\"等有空了再还\"的意思是永远不还。",
          "",
          "做法：把还债变成迭代的固定项：每个迭代划出固定比例的时间（参考实践：20% 左右）专门还债，还债任务和功能任务一样有验收标准、有 review、有记录。把\"还债\"从\"额外工作\"变成\"常规工作\"。老板视角的话术：还债不是烧钱，是给系统买保险——今天不还，利息每天在涨。",
          "",
          "为什么：技术债是持续产生的（每个赶工的需求都在产生新债），所以还债也必须持续。固定比例的机制化安排，让还债不依赖某个人\"有良心\"、不依赖某个版本\"恰好有空\"。它是唯一能跑赢\"利息复利\"的方式。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 先扫描，后动手：SonarQube（或同类工具）出一张技术债热力图，建清单，四象限排序；",
          "2. 给关键路径补测试：核心业务 + 高危代码先包住，覆盖率 80% 再动核心；",
          "3. 选一个独立小模块试点绞杀者模式：新架构写新功能，老模块一块块迁，防腐层隔离；",
          "4. 小步提交：单次重构 ≤50 行，每步 CI 全绿 + review，随时可回滚；",
          "5. 把还债排进迭代：每迭代固定 20% 时间，还债任务有验收、有记录、有 review。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目**：没有老板和团队，你的债主是你自己——定期用扫描工具看看自己的代码，至少把\"每次改动都要靠猜\"的模块补上测试；重构前 `git commit` 一个干净基线，改坏了随时回退。",
          "- **小团队（5-15 人）**：技术债清单建起来，每次迭代划固定比例时间还债；别搞\"重构周\"——集中式还债既影响业务又容易烂尾，细水长流才是正解。",
          "- **大型系统 / 核心业务**：只走绞杀者模式，防腐层隔离，迁移顺序按业务价值×风险排；任何一次 Big Bang 重写提案，先问三个问题：老系统的边界情况你盘点全了吗？测试保护网建好了吗？业务能接受几个月的不稳定吗？答不上来就别立项。",
          "- **AI 时代**：AI 是重构的好帮手（识别代码异味、批量替换、生成测试），但\"重写\"的诱惑在 AI 时代更大了——AI 生成新系统的速度会让你误以为重写成本很低。清醒一点：AI 帮你写的也是新代码，老系统的业务逻辑它同样看不见。让 AI 做绞杀者模式里的\"手术刀\"，别让它做\"推土机\"。",
          "- **如果你正在犹豫\"要不要重写\"**：先做经验 1 和 3——把债列出来，把测试补上。做完这两步，你大概率会发现：真正需要\"重写\"的部分，远没有你想象的那么多；而剩下的债，绞杀者模式足够还清。",
          "",
          "收尾：我栽过最大的跟头，是相信「重构完就清爽了」——结果重写两个月，老系统的边界情况在第四周开始报复我，最后灰溜溜回滚。后来我才想明白：技术债不是「脏东西要一次性清掉」，它是「你欠未来的利息，得按月还」。别等大扫除——先把债列出来、把测试补上，剩下的交给绞杀者模式慢慢磨。你不需要一次还清，你只需要今天开始还。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/tech-debt-consensus",
        "agent": "https://trove.xin/agent/problems/tech-debt-consensus"
      }
    },
    {
      "id": "tls-jdk-upgrade",
      "title": "JDK 老项目升级 TLS 握手失败：Received fatal alert: protocol_version / No appropriate protocol",
      "domain": "security",
      "domain_label": "安全与治理",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "jdk",
        "openssl"
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "source": "https://www.cnblogs.com/xsge/p/22703862",
      "recorded": "2026-08-30",
      "sections": {
        "你会遇到什么": [
          "",
          "老项目（JDK 1.6/1.7/1.8 早期）升级依赖、迁移服务器或开启 HTTPS 后，HTTPS 调用突然失败：",
          "",
          "- 客户端抛 `javax.net.ssl.SSLHandshakeException: Received fatal alert: protocol_version` 或 `No appropriate protocol (protocol is disabled or cipher suites are inappropriate)`；",
          "- 服务端 `openssl s_client -connect <host>:<port> -tls1` 能通，但 `-tls1_2` 连不上（服务端只支持旧协议）；或反过来，新服务端拒绝老客户端。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "TLS 握手要求客户端与服务端的「可用协议集合」交集非空；交集为空时服务端发 `alert protocol_version`（alert 号 70），Java 侧就是 SSLHandshakeException。三种典型坑：",
          "",
          "| 坑 | 机制 |",
          "|---|---|",
          "| 服务端只开 TLSv1.0/1.1 | 新 JDK/OpenSSL 默认已禁旧协议，客户端没有可协商协议 |",
          "| 老 JDK 默认最高 TLSv1.0 | JDK 1.7 及以下默认不启用 TLS1.2，连不上只支持 TLS1.2+ 的新服务端 |",
          "| 代码/配置硬编码旧协议 | `setEnabledProtocols(\"TLSv1\")`、SSLv3、自定义 SSLContext、跳过主机名验证 |",
          "",
          "JDK 各版本默认能力（原文详表摘要）：",
          "",
          "| JDK 版本 | 默认 TLS 能力 | 关键点 |",
          "|---|---|---|",
          "| 1.6 | 最高 TLSv1.0 | 上 TLS1.2 需换 JDK |",
          "| 1.7.0_79 | 支持 TLS1.2 但默认未启用 | 手动启用 + 禁 TLSv1/1.1 |",
          "| 8u112 | TLS1.2；不支持 TLS1.3 | TLSv1/1.1 默认未禁用，需手动禁 |",
          "| 8u261~8u340 | TLS1.3 服务端默认启用 | 8u341+ 客户端也默认启用 |",
          "| 11.0.0~11.0.10 | TLS1.2/1.3；TLSv1/1.1 未禁 | 11.0.11+ 默认禁 TLSv1/1.1 |",
          "",
          "> JDK 11 早期 TLS1.3 有会话恢复/死循环 bug（JDK-8211806 不发 SNI、JDK-8212885 证书链丢失、JDK-8213202 竞态、JDK-8214418 100% CPU），11.0.8 前不建议直接用 TLS1.3。",
          "",
          "TLS 握手是「协议集合交集」协商：客户端把自己支持的协议列表发给服务端，服务端选交集；交集为空就发 `alert protocol_version`（70），Java 侧表现为 SSLHandshakeException。",
          "",
          "问题永远出在「某一侧的能力集合」与时代脱节：服务端只开 TLSv1.0/1.1（新客户端默认已禁）或客户端太老（JDK ≤1.7 默认最高 TLSv1.0，连不上只支持 1.2+ 的服务端），或代码硬编码旧协议。",
          "",
          "JDK 版本默认能力随时间演进（8u112 支持 1.2 不支持 1.3；8u341+ 客户端默认启用 1.3；11.0.11+ 默认禁 1.0/1.1）。",
          "",
          "确定性推论：握手失败先确定两侧能力——服务端 `openssl s_client -tls1_2 -connect <host>:<port>` 测协议；客户端 `-Djavax.net.debug=ssl:handshake` 看发送的协议列表；修法按侧：服务端开新协议/客户端升级 JDK/代码去掉硬编码协议；",
          "",
          "老 JDK 上 1.3 有已知 bug（JDK-8211806 等），11.0.8 前不建议直接用；",
          "",
          "判断路径：alert 70 = 协议交集为空，不是证书/密码套件问题。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 客户端全局禁用旧协议（改 java.security，最常用）",
          "# $JAVA_HOME/jre/lib/security/java.security（Oracle）或 $JAVA_HOME/conf/security/java.security（OpenJDK）",
          "jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, MD5withRSA, DH keySize < 768",
          "",
          "# 只影响当前应用（JVM 启动参数；-Dhttps.protocols 只管 HttpsURLConnection，",
          "# -Djdk.tls.client.protocols 管全局客户端）",
          "-Dhttps.protocols=TLSv1.2,TLSv1.3 -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3",
          "",
          "# 服务端侧（nginx）",
          "ssl_protocols TLSv1.2 TLSv1.3;",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先判断哪边有问题 — **必做**",
          "",
          "```bash",
          "# 对目标 HTTPS 端口三连测：1.2 通 / 1.0、1.1 不通 → 服务端只支持新协议，问题在客户端",
          "openssl s_client -connect <host>:<port> -tls1_2",
          "# 1.0 通 / 1.2 不通 → 服务端还是旧协议，先升级服务端",
          "openssl s_client -connect <host>:<port> -tls1",
          "```",
          "",
          "### 2. 客户端：禁用旧协议（三种粒度） — **必做**",
          "",
          "- 全局（推荐）：java.security 的 `jdk.tls.disabledAlgorithms` 加 `TLSv1, TLSv1.1`，重启应用；影响该 JDK 上所有应用，若有老应用依赖 TLSv1/1.1 会连失败，先确认。",
          "- JVM 参数（当前应用）：`-Dhttps.protocols=TLSv1.2,TLSv1.3 -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3`。",
          "- 代码层（单连接）：`connection.setEnabledProtocols(new String[]{\"TLSv1.2\", \"TLSv1.3\"})`；Spring Boot 用 `server.ssl.enabled-protocols: TLSv1.2, TLSv1.3`。",
          "",
          "### 3. 排查硬编码与跳过校验 — **排查**",
          "",
          "```bash",
          "grep -rn \"setHostnameVerifier\" --include=\"*.java\" .",
          "grep -rn \"TLSv1\\|SSLv3\" --include=\"*.java\" .",
          "```",
          "",
          "重点：HttpsURLConnection.setEnabledProtocols、Apache HttpClient 的 SSLConnectionSocketFactory 协议列表、RestTemplate 自定义 SSL 上下文、生产环境严禁跳过证书验证的 HostnameVerifier。",
          "",
          "### 4. 验证 — **必做**",
          "",
          "```bash",
          "openssl s_client -connect <host>:<port> -tls1_2   # 应该成功",
          "openssl s_client -connect <host>:<port> -tls1     # 应该失败",
          "openssl s_client -connect <host>:<port> -tls1_1   # 应该失败",
          "```",
          "",
          "应用日志无 SSLHandshakeException / NoSuchAlgorithmException，打印的协议版本为 TLSv1.2。"
        ],
        "怎么确认修好了": [
          "",
          "三连测结果符合「1.2 通、1.0/1.1 拒」，且应用日志握手版本为 TLSv1.2+；Java 侧不再抛 `Received fatal alert: protocol_version`。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 服务端（nginx/openssl） | `ssl_protocols TLSv1.2 TLSv1.3;`，等价于实测中的「新服务端」场景 |",
          "| OpenSSL 3.5 | 默认安全级已禁 TLSv1.0/1.1，与 JDK 8u261+/11.0.11+ 默认禁旧协议同机制 |",
          "| 未实测 | JDK 1.6/1.7/8 各版本默认行为表来自原文，本机无老 JDK 未复现 |",
          ""
        ],
        "症状": [
          "",
          "HTTPS 调用报 `SSLHandshakeException: Received fatal alert: protocol_version` 或 `No appropriate protocol`；openssl 三连测中 TLS1.0/1.1 与 TLS1.2 结果不对称（一边通一边不通）。",
          ""
        ],
        "原因": [
          "",
          "客户端与服务端可用 TLS 版本集合交集为空：服务端仅旧协议（TLSv1.0/1.1）、客户端默认禁旧协议；或老 JDK 默认最高 TLS1.0 连新服务端；或代码硬编码旧协议。",
          ""
        ],
        "解法": [
          "",
          "1. openssl 三连测定位哪边受限（`-tls1` / `-tls1_1` / `-tls1_2`）；",
          "2. 服务端：nginx `ssl_protocols TLSv1.2 TLSv1.3;`，升级老 openssl；",
          "3. 客户端：java.security `jdk.tls.disabledAlgorithms` 加 `TLSv1, TLSv1.1`（全局），或 `-Djdk.tls.client.protocols=TLSv1.2,TLSv1.3`（当前应用），或代码 `setEnabledProtocols`（单连接）；",
          "4. 排查并清理 `TLSv1`/SSLv3 硬编码与跳过校验的 HostnameVerifier；",
          "5. 三连测验证：1.2 通、1.0/1.1 拒。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制实测（2026-08-30，WSL Ubuntu，OpenSSL 3.5.5）：本地 TLS1.0-only 服务端 → TLS1.2 客户端握手失败（`unsupported protocol`）；TLS1.0 客户端成功。反向 TLS1.2-only 服务端 → TLS1.0 客户端报 `tlsv1 alert protocol version`（即 Java 侧 `Received fatal alert: protocol_version` 的底层），TLS1.2 客户端成功。证据：实测复现日志（2026-08-30）。",
          "- 未实测：本机无 JDK 1.6/1.7/8，版本默认行为表来自原文《万字长文！TLS协议升级避坑手册》（净重21克，2026-08-26），未在本机复现；OpenSSL 3.5 默认安全级已禁 TLS1.0/1.1，与新版 JDK 默认禁旧协议机制相同。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [GitHub 直连慢/失败](github-direct-connect-fail.html)（报错相近（TLS 断连），根因不同）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/tls-jdk-upgrade",
        "agent": "https://trove.xin/agent/problems/tls-jdk-upgrade"
      }
    },
    {
      "id": "uninstall-leftover",
      "title": "卸载后残留——共享配置不还原 / 命令仍在",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "install-deps",
      "category_label": "安装与依赖",
      "applies_to": [
        "cc-switch",
        "claude-code-router"
      ],
      "evidence": "Evidence details kept locally",
      "source": "结构重组合并（原 cc-switch-6 + claude-code-router-7，2026-08-29）",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "卸载工具后残留问题仍在：共享配置没还原（之前被工具改写的 `~/.claude.json` / `~/.codex/config.toml` 保持切换后的状态）；命令仍在（卸载后 `ccr` / `cc-switch` 仍能解析，或重装后配置还是旧的）。卸了等于没卸干净。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "卸载命令只删工具自身（`dpkg -r` / `npm rm -g` + 工具自己的配置目录），**不还原它改过的共享配置文件**；shell hash 缓存会让已卸载的命令继续命中。",
          "",
          "卸载 ≠ 还原：卸载程序（`dpkg -r` / `npm rm -g`）只删除**它自己声明拥有的文件**（二进制、库、自己的配置目录），不会还原它安装时改过的共享配置文件（如追加过配置项的系统级文件、PATH 写入、启动器注册）。所以「卸载后还在影响系统」是常态而不是异常。",
          "",
          "shell hash 缓存是第二层：bash 会把命令路径缓存在内存里，已卸载的命令在**同一个 shell 会话**里仍可能被命中执行（新开终端才消失）。",
          "",
          "确定性推论：卸载后残留影响按两个方向查——文件层（`which <cmd>`、`dpkg -l | grep`、配置目录）和环境层（PATH、shell hash：`hash -r` 清缓存）；「卸载了还能跑」先 `hash -r` 或开新终端排除缓存，再查 PATH 里的残留文件；判断路径：同一终端能跑/新终端不能 = hash 缓存，两边都不能 = 文件残留。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：卸载前先备份共享配置（唯一后悔药）",
          "cp ~/.claude.json ~/.claude.json.bak",
          "```",
          "",
          "完整卸载 = 删包 + 删工具配置目录 + 手动还原共享配置段。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 卸载前备份共享配置 — **必做**",
          "",
          "```bash",
          "cp ~/.claude.json ~/.claude.json.bak",
          "```",
          "",
          "同理 `~/.codex/config.toml`、`~/.claude/settings.json`。",
          "",
          "### 2. 完整卸载 = 删包 + 删工具配置目录 — **必做**",
          "",
          "cc-switch：",
          "",
          "```bash",
          "dpkg -r cc-switch && rm -rf ~/.cc-switch",
          "```",
          "",
          "claude-code-router：",
          "",
          "```bash",
          "npm rm -g @musistudio/claude-code-router && rm -rf ~/.claude-code-router",
          "```",
          "",
          "### 3. 手动还原共享配置段 — **必做**",
          "",
          "还原 provider 段 / ANTHROPIC_* 路由注入段，或按备份还原；**共享文件不代删**（其它工具也在写）。",
          "",
          "### 4. 新 shell 验证 — **必做**",
          "",
          "```bash",
          "command -v ccr",
          "```",
          "",
          "应为空；重装后配置应是新的。"
        ],
        "怎么确认修好了": [
          "",
          "零残留验证（实测输出形态）：",
          "",
          "```text",
          "ccr uninstall 闭环：102 包移除 + 配置目录清除 → verify 零残留 exit=0（实测）",
          "command -v ccr → 空",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| cc-switch | 卸载命令不含还原步骤（字符串审计）；dpkg -r + rm -rf ~/.cc-switch |",
          "| claude-code-router | npm rm + 删 ~/.claude-code-router；settings.json 路由注入段手动确认（实测闭环） |",
          "| 共享配置 | 先备份再动手；不代删共享文件 |",
          "| shell hash | 新 shell 验证 command -v |",
          ""
        ],
        "症状": [
          "",
          "卸载工具后残留问题仍在：",
          "",
          "- 共享配置没还原：之前被工具改写的 `~/.claude.json` / `~/.codex/config.toml` 保持切换后的状态；",
          "- 命令仍在：卸载后 `ccr` / `cc-switch` 仍能解析，或重装后配置还是旧的。",
          ""
        ],
        "原因": [
          "",
          "卸载命令只删工具自身（`dpkg -r` / `npm rm -g` + 工具自己的配置目录），不还原它改过的共享配置文件；shell hash 缓存会让已卸载的命令继续命中。",
          ""
        ],
        "解法": [
          "",
          "1. 卸载前备份共享配置：`cp ~/.claude.json ~/.claude.json.bak`（同理 `~/.codex/config.toml`、`~/.claude/settings.json`）。",
          "2. 完整卸载 = 删包 + 删工具配置目录：cc-switch 用 `dpkg -r` + `rm -rf ~/.cc-switch`；claude-code-router 用 `npm rm -g @musistudio/claude-code-router` + `rm -rf ~/.claude-code-router`。",
          "3. 手动还原共享配置段（provider 段 / ANTHROPIC_* 路由注入段）或按备份还原；共享文件不代删（其它工具也在写）。",
          "4. 新 shell 验证：`command -v ccr` 应为空；重装后配置应是新的。",
          ""
        ],
        "受影响工具实例": [
          "",
          "- **cc-switch**：卸载 = `dpkg -r` + `rm -rf ~/.cc-switch`，不含还原步骤（字符串审计）；实测闭环。",
          "- **claude-code-router**：npm 全局包 + `~/.claude-code-router` 配置目录；`~/.claude/settings.json` 路由注入段需手动确认清理；桌面端 release 二进制走系统卸载，不在 npm 管理范围；实测闭环 2026-08-28（102 包移除 + 目录清除 + verify 零残留 exit=0）。",
          ""
        ],
        "验证证据": [
          "",
          "- cc-switch：字符串审计确认管理面含 `~/.claude.json` / `~/.codex/config.toml` / opencode.json；卸载命令不含还原步骤（manifest uninstall.note）。",
          "- claude-code-router：实测闭环 2026-08-28：install exit=0 → uninstall（102 包移除 + 配置目录清除）→ verify 零残留 exit=0。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/uninstall-leftover",
        "agent": "https://trove.xin/agent/problems/uninstall-leftover"
      }
    },
    {
      "id": "unit-testing-consensus",
      "title": "单元测试覆盖率 80% 了，线上还是老出问题——覆盖率到底该怎么用？",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-09-01",
      "sections": {
        "症状": [
          "",
          "先讲一个让我后背发凉的真实案例：一个团队用 SonarQube 卡着 60% 的覆盖率红线，每次合并请求都规规矩矩跑单元测试——但生产环境的 P0 故障，从每季度 1 次飙升到每月 3 次。",
          "",
          "我当时被请去做敏捷转型咨询，第一反应是\"覆盖率是不是没执行到位？\"查完数据我才发现：整体覆盖率 71%，漂亮；但每个迭代新增的 3500 行代码和修改的 1800 行代码，那些变更文件的平均覆盖率只有 47%——而直接导致 P1 故障的变更，当时的覆盖率中位数只有 23%。**团队不是没有测试，是把测试全部写在了两年没动过的老代码上，而真正每天都在变、最容易出错的代码，几乎是裸奔。**",
          "",
          "更讽刺的是另一种\"死法\"：覆盖率数字越管越高，线上问题越来越多。团队为了达标，把精力花在给稳定模块补测试、给 getter/setter 凑覆盖率上；开发被指标逼着刷数字，测试从\"质量保障\"变成\"数字游戏\"。",
          "",
          "有研究对 GitHub 上 7000 多个 Java 项目做过实证：覆盖率从 0% 提到 60-70% 时，缺陷密度明显下降；但过了这个阈值，新增覆盖率的边际收益急剧衰减，甚至出现\"覆盖率越高、缺陷越多\"的反常现象。",
          "",
          "还有一种我特别熟悉的死法：测试变成负担，团队集体放弃。测试绑死了实现细节——内部变量换个结构就全红；mock 复杂到改不动；没人看得懂测试到底在测什么。于是大家心照不宣：失败就注释掉、跳过、删掉。测试从\"安全网\"变成了\"负债\"。",
          "",
          "如果你也在经历这些——覆盖率达标了线上还是炸；或者为了达标疯狂补测试、补完没人维护；或者测试已经变成\"红色就注释掉\"的仪式——这篇文章是写给同类人的。它不是测试教程，是那些真被覆盖率骗过、也真把测试做成安全网的人，用事故和数据换来的共识：**覆盖率是后视镜，不是前挡风玻璃——它告诉你\"去过哪些路\"，不告诉你\"前面的路安不安全\"。**",
          ""
        ],
        "原因": [
          "",
          "先想清楚：覆盖率这个指标，到底在测什么？",
          "",
          "**覆盖率只回答一个问题：哪些代码被执行过。** 它回答不了三件更重要的事：①被执行过的代码，是否被真正验证了正确性（断言了吗？还是只是跑了一遍）；②没被执行过的代码里，藏着什么风险；③变更相关的代码，有没有被覆盖。把覆盖率当质量目标，等于用\"我开过这条路\"来证明\"这条路安全\"——这两件事的相关性，远没有你以为的高。",
          "",
          "为什么团队会集体掉进\"覆盖率越高越好\"的坑？因为它是**最容易度量、最不需要判断力的质量指标**。管理层要质量，团队要给数字，覆盖率是双方都看得懂的\"安全凭证\"。但正是这种\"容易\"，让它变成了最危险的自我欺骗工具：",
          "",
          "**第一，覆盖率把注意力从\"变更风险\"转移到了\"代码覆盖\"。** 敏捷迭代的核心活动是新增和修改代码，不是维护老代码。当团队把 80% 的测试精力花在两年没改过的稳定模块上，只留 20% 给本轮迭代的新增和修改代码，质量崩塌只是时间问题。",
          "",
          "**第二，覆盖率 KPI 会制造\"测试表演\"。** 一旦覆盖率变成考核指标，最优策略就是\"挑容易覆盖的代码补测试\"：getter/setter、工具函数、稳定模块——这些测试写起来快、覆盖率涨得快，但对真实风险毫无帮助。测试从质量保障变成数字游戏，这就是\"testing becomes theater\"。",
          "",
          "**第三，脆弱的测试会自我毁灭。** 测试绑死实现细节（内部字段、私有结构），重构一下全红；mock 复杂到没人敢改；断言缺失的\"执行型测试\"只跑不验。这样的测试维护成本超过收益，团队就会用脚投票：注释、跳过、删除——最后连\"知道哪里出了问题\"的能力都丢了。",
          "",
          "还有一个所有人都知道但没人敢说的真相：**没有测试的代码是\"不敢改\"的代码，而不敢改的代码，才是最大的技术债源头。** 测试的真正价值从来不是那个百分比数字，而是它给你的\"敢动\"的自由。下面每条经验，都是把覆盖率从\"数字游戏\"拉回\"变更保护\"的做法。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：覆盖率是后视镜——关注\"变更覆盖率\"，别追\"总覆盖率\"",
          "",
          "**总覆盖率是后视镜：它告诉你过去去过哪，不告诉你接下来安不安全。真正该盯的是\"本轮变更的代码有没有被测试保护\"。** 赶时间只看这一条：把质量门禁从\"总覆盖率 ≥80%\"改成\"本次变更的增量覆盖率 ≥80%\"，并且只对变更相关的测试负责。下面经验 1~5 是为什么和怎么做。",
          "",
          "### 经验 1：测变更，不测存量——增量覆盖率才是发版决策的依据",
          "",
          "现象：全量覆盖率卡发布是每个团队都经历过的噩梦：发布前发现总覆盖率差 2%，于是花三天给几年没动的老代码补测试——这些代码已经稳定运行很久，补测试的风险大于收益，改完还可能引入新 bug。与此同时，本轮真正新增和修改的高风险代码，覆盖率可能只有 20%。",
          "",
          "做法：门禁从\"全量覆盖率\"改成\"增量覆盖率\"：解析本次变更的文件，只统计这些文件的新增/修改行有没有被测试覆盖，达标才允许合并。有团队实测：把门禁从\"全量 85%\"改成\"增量 85%\"后，开发开始边写业务代码边写测试，发布不再被老代码卡脖子，质量门禁第一次变得有意义。参考线：增量覆盖率 ≥80%，高危模块（资金、权限、数据一致性）≥90%。",
          "",
          "为什么：增量覆盖率回答的是\"这次变更会不会把线上搞坏\"——这才是发版决策真正需要的信息。全量覆盖率回答的是\"过去积累的测试资产有多少\"，它对\"这次发布安不安全\"几乎没有预测力。GitHub 7000 项目的实证数据早就说了：过了 60-70% 的阈值，全量覆盖率的边际价值趋近于零；而变更代码的覆盖率，永远在阈值以下。",
          "",
          "### 经验 2：别把覆盖率当 KPI——指标会骗人，测试会表演",
          "",
          "现象：覆盖率一旦进 KPI，就会产生\"测试表演\"：开发挑最便宜的代码补测试（getter/setter、工具函数、稳定模块），覆盖率数字一路上涨，线上缺陷数纹丝不动甚至上涨。有个团队用 60% 红线卡了半年，P0 从季度 1 次变每月 3 次——不是覆盖率没用，是他们把\"覆盖率\"当成了\"质量\"本身。",
          "",
          "做法：覆盖率只当\"体检指标\"，不进绩效考核。考核看什么？看结果指标：线上缺陷率、变更引入的回归数、测试拦下的问题数。给团队留出\"测试该写在哪\"的判断空间，而不是用一刀切的数字逼所有人刷分。把\"覆盖率 80%\"从命令改成对话：\"这轮变更的哪部分风险最高？测试覆盖到了吗？\"",
          "",
          "为什么：人只会对\"被衡量的事\"负责。你把覆盖率变成 KPI，团队就会优化覆盖率数字，而不是优化质量——这是 Goodhart 定律的经典案例：当一个指标变成目标，它就不再是好指标。真正的质量指标应该是\"测试有没有拦住过事故\"，而不是\"测试跑了多少行\"。",
          "",
          "### 经验 3：面向行为写测试——测试绑实现细节，等于给自己埋雷",
          "",
          "现象：测试维护难的头号原因：测试绑死了实现细节。内部价格表从 List 换成别的结构，测试全红；mock 链复杂到改一个参数要动五个文件；断言缺失的\"执行型测试\"只跑不验，红灯了也不知道是逻辑错还是测试错。这样的测试，改一次痛一次，最后被团队集体注释掉。",
          "",
          "做法：测试面向\"可观察行为\"，不面向\"内部结构\"：只依赖公共 API；mock 放在外部边界（外部服务、数据库、时间），不 mock 内部逻辑；每个测试至少一个有效断言，断言\"系统做了什么\"，不断言\"系统怎么做\"。",
          "",
          "\"脆弱测试\"长这样：直接操作 `InternalPriceList` 内部字段；\"健壮测试\"长这样：通过公开方法加商品、用外部服务的假实现返回价格、断言订单总额——同样的业务，后者的维护成本是前者的零头。",
          "",
          "为什么：实现细节是代码里最常变的部分，测试绑死它，等于每次重构都要重写测试——测试就变成了重构的阻力而不是助力。面向行为的测试把\"内部怎么实现\"留给了重构的自由空间：只要对外行为不变，测试就全绿，你才敢放心改。",
          "",
          "### 经验 4：金字塔别倒——单元测试是底座，别让 E2E 扛大梁",
          "",
          "现象：很多团队的测试结构是\"倒金字塔\"：E2E 测试一大堆，单元测试寥寥无几。E2E 跑得慢、不稳定、维护成本高——前端按钮 ID 一改，几十条用例一起红。而且 E2E 覆盖了很多代码路径，会虚高你的覆盖率数字，让你误以为\"测得很全\"。",
          "",
          "做法：按金字塔配比搭测试结构：底层是大量快速稳定的单元测试（覆盖面最广），中间是适量集成测试（验证模块间协作），顶层是少量关键路径的 E2E（验证核心用户旅程）。E2E 只保\"最重要的事\"，别什么都往 E2E 里塞。参考线：一个迭代里新增的测试，80% 应该是单元级，E2E 只加\"核心链路\"级别的。",
          "",
          "为什么：测试金字塔的本质是\"用最便宜的测试覆盖最多的地方\"。单元测试毫秒级、定位准、好维护；E2E 分钟级、定位难、一坏坏一片。把 E2E 当主力，等于用最贵的工具干最多的事——成本撑不住，最后整个测试体系垮掉。倒金字塔的团队，先补单元测试的底座，比加更多 E2E 有用十倍。",
          "",
          "### 经验 5：AI 时代，测试是最后一道护栏——AI 写代码，你写测试",
          "",
          "现象：2025 年之后出现了一个更隐蔽的滑坡：团队用 AI 写代码，同时把\"测试\"也外包了——甚至因为 AI 代码改得太快，干脆不写测试、不看测试。结果就是：AI 生成的代码合并得飞快，回归全靠运气，bug 悄悄溜进生产。AI 让\"写代码\"变便宜了，也让\"不验证\"的代价变大了。",
          "",
          "做法：AI 时代的测试纪律——①AI 生成的代码，测试必须人写（或人审）：AI 的测试和它的代码一样\"看起来对\"，但同样不知道业务到底要什么；②AI 改代码，先跑测试再合：把\"全绿\"设为 AI 合并的前置条件，谁改的都要过；③用 AI 补测试可以，但断言要人定：AI 帮你生成测试骨架、mock 边界，但\"什么是对的\"必须人来断言。",
          "",
          "为什么：AI 最大的特点是\"快\"，最大的风险也是\"快\"——错误传播的速度前所未有。测试是唯一能拦住\"AI 看起来很对、实际上不对\"的关卡。把测试外包给 AI 等于把最后一道关卡也交给同一个会犯错的模型。人写断言、机器跑验证，这才是 AI 时代性价比最高的配合。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 门禁改增量：质量门禁从\"总覆盖率 ≥80%\"改成\"本次变更增量覆盖率 ≥80%\"，高危模块 90%；",
          "2. 覆盖率出 KPI：考核看线上缺陷率、回归数、测试拦下数，不看覆盖率数字；",
          "3. 面向行为写测试：只依赖公共 API，mock 外部边界，每个测试至少一个有效断言；",
          "4. 金字塔配比：新增测试 80% 单元级，E2E 只保核心链路；",
          "5. AI 代码测试纪律：AI 合并前置\"测试全绿\"，断言必须人写人审。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目**：你既是开发又是测试——把\"关键路径测试\"当作重构前的保险：改代码前先把最常碰的模块用测试固定住（哪怕先补 3-5 个核心用例），你就获得了\"敢改\"的自由；覆盖率不用追，追\"我改的东西有没有被测过\"。",
          "- **小团队（5-15 人）**：增量覆盖率门禁 + 面向行为写测试这两条先落地，就能解决 80% 的\"测试没用\"问题；别急着上覆盖率 KPI——先让测试从\"负担\"变成\"保护\"，再谈规模化。",
          "- **大型团队**：覆盖率红线最容易在大型团队变成表演——把考核指标换成结果指标（缺陷率、回归数），用增量覆盖率做门禁，用金字塔配比约束测试结构；每季度审视一次\"测试资产里有多少是真正保护变更的\"。",
          "- **AI 时代**：AI 写代码已经是常态，测试纪律要更硬：AI 合并前置测试全绿，断言人写人审；把\"AI 补测试\"当成加速器而不是甩锅口——AI 可以帮你生成骨架，但\"什么是对的\"永远是人说了算。",
          "- **如果你正卡在\"覆盖率达标了还是炸\"**：先别加测试，先看两个数——本轮变更的增量覆盖率是多少？P1 故障对应的变更当时覆盖率是多少？大概率你会看到：不是测试不够，是测试没写在正在变的地方。",
          "",
          "收尾：覆盖率 80% 还出事，不是覆盖率骗你，是你没测对地方。我在生产事故里学到的一课：最有价值的测试，永远是「这次改动碰过的代码」的测试。别再盯着总覆盖率自我感动了——改代码之前，先确认你这次要动的东西有没有测试兜底；没有就先补三五条。AI 时代这条更重要：AI 负责写，测试负责拦，而「写测试」这件事，暂时还得靠人。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/unit-testing-consensus",
        "agent": "https://trove.xin/agent/problems/unit-testing-consensus"
      }
    },
    {
      "id": "vibe-coding-consensus",
      "title": "Vibe Coding 到底靠不靠谱？AI 写代码快但容易翻车，怎么用才稳",
      "domain": "experience",
      "domain_label": "经验与方法论",
      "category": "experience",
      "category_label": "经验与方法论",
      "applies_to": [],
      "evidence": "共识型经验（多篇同主题来源提炼，案例已脱敏融入正文，不逐一列出）",
      "source": "",
      "recorded": "2026-08-30",
      "sections": {
        "症状": [
          "",
          "先交代一段黑历史。",
          "",
          "我刚开始重度用 AI 写代码时，干过一件现在想起来都后怕的事：AI 给我生成了一段\"结构规整、注释完美、命名优雅\"的代码，连空行都排得整整齐齐。我看了一眼：\"逻辑挺清晰的\"，直接合进去了。",
          "",
          "三天后线上出问题。我回去翻那段代码，业务逻辑根本就是错的——它把\"删除\"分支写成了\"覆盖\"分支。而我的测试只断言了状态码，全绿。",
          "",
          "那一次让我明白：**\"看起来对\"和\"真的对\"之间，隔着一整条验证链，而我把那条链跳过了。**",
          "",
          "这不是我一个人的毛病。后来团队里年轻的同事开始频繁提交这种\"看着就舒服\"的代码——结构规整、注释完美、命名优雅。可 review 时往深里一看，业务逻辑跑不通的占了不少。问他们怎么写的，答案出奇地一致：\"这是 AI 给的代码，我看它逻辑挺清晰的，就直接拿来用了。\"",
          "",
          "更刺激的一次：我们把 Coding Agent 放进生产系统，不到两个工作日就产出两三万行代码——需求理解、方案设计、功能开发、测试文档一次全齐，效率是过去不敢想的。",
          "",
          "但系统是 7×24 小时连续运转的交易场景，数据不以自然日为边界。跨天、跨时间分区的计算需求，Agent 一行都没考虑。上线即翻车，我们连夜回滚。",
          "",
          "如果你也在经历类似的事——AI 写代码飞快，但代码质量开始让你心里打鼓；或者你正打算全面拥抱\"100% Vibe Coding\"——这篇文章值得读完。它不是一个工具的安装教程，而是我们这些在真实项目里用 AI 写代码、踩过坑的人，用事故换来的共识。",
          ""
        ],
        "原因": [
          "",
          "先搞清楚 AI 写代码的本质：**它是模式匹配器，不是工程师。**",
          "",
          "大模型没有\"理解\"，它是在海量代码语料上做统计预测。好代码长什么样，它就生成什么样——结构规整、注释齐全、命名优雅，因为这些特征在训练数据里与\"好代码\"强相关。",
          "",
          "所以它特别擅长产出\"看起来正确\"的代码。而\"看起来正确\"和\"真的正确\"之间，隔着一整条验证链。",
          "",
          "这里有个更隐蔽的机制：**速度掩盖了质量问题。**",
          "",
          "过去\"写代码慢\"本身就是一道质量闸门——人写得慢，就有时间想；提交得少，review 就认真。现在生成速度被拉高了几十倍，闸门没了：代码产出量大到 review 不过来，而 AI 的每个局部改动看起来都\"没毛病\"。",
          "",
          "问题是，**复杂度只是转移了，并没有消失**——它从\"写不出来\"转移到了\"看不出错\"，从代码本身转移到了边界条件和系统语义里。我的\"删除变覆盖\"事故，就是这条机制的现场演示：单看每一行都合理，组合在一起就是事故。",
          "",
          "还有一个心理层面的坑：AI 厂商把模型语气调得极其谦卑，动不动\"抱歉，您说得对，我马上改\"。人的大脑会不自觉地把它当成一个\"态度很好、有点粗心的初级程序员\"，套用对待实习生的宽容——\"它都这么努力理解我了，大体方向应该没错\"。",
          "",
          "于是 review 雷达关闭。而现实是：态度诚恳和代码正确，毫无关系。我见过最离谱的一次，是同事对着 AI 说\"这个 bug 改得不对\"，AI 诚恳道歉三连之后，交上来的还是同一个错误版本。",
          "",
          "连 Vibe Coding 这个词的提出者 Andrej Karpathy（OpenAI 联合创始人）后来也公开转向了更务实的 Agentic Engineering。连造词的人都刹车了，\"100% Vibe Coding 颠覆编程\"的叙事，你最好也打个问号。",
          ""
        ],
        "解法": [
          "",
          "下面每条经验都按「现象 → 做法 → 为什么」展开。它们不是一家之言，而是多篇独立实践里反复出现的共识。",
          "",
          "### 经验 0（L1 一句话结论）：Vibe Coding 能用，但\"100% 交给 AI\"是事故配方",
          "",
          "AI 负责快，你负责对。**凡是\"看起来挺对就直接用\"的流程，早晚会出事；凡是\"AI 写 + 人验 + 小步走\"的流程，稳定省时间。**赶时间只看这一条：给 AI 代码设硬门槛——review + 测试一条都不少，复杂系统自己把关。下面的经验 1~5 是这条结论为什么成立的细节。",
          "",
          "### 经验 1：拒绝拟人化——它是 Token 机器，不是你的同事",
          "",
          "现象：我一度也说过这种话——\"今天这模型好像变笨了，没听懂我的需求\"\"我指出了它的 Bug，它认错的态度还挺诚恳的\"。听起来是玩笑，但这种习惯很致命：模型语气被微调得极其谦卑，这种讨好型输出会在潜意识里给你一种错觉——你在和一个脾气极好、非常听话的初级程序员沟通。",
          "",
          "人类协作里，我们对同事是有容错率的：实习生态度好，代码有瑕疵，你会下意识宽容他、甚至帮他把逻辑补齐。当你把大模型拟人化之后，大脑会不自觉地套用这套社交直觉，放松警惕：\"它都这么努力理解我了，大体方向应该没错。\"我的那次事故，起点就是这种放松。",
          "",
          "做法：不赋予 AI 情感、意图、道德代理权。把它当成一个高级的命令行工具，或者一个随时会抛出 `NullPointerException` 的第三方黑盒依赖。不需要\"请\"和\"谢谢\"，不需要揣摩它\"想\"干什么。",
          "",
          "为什么：拟人化会直接让你的 Code Review 雷达失效。剥掉这层滤镜，你才不会对它生成的代码抱有任何不切实际的信任感。",
          "",
          "### 经验 2：验证大于信任——\"看起来规整\"恰恰是 AI 代码的伪装",
          "",
          "现象：有团队做过对照实测：AI 写代码的速度快了 55%，但错误多了 75%。我自己也踩过类似的反例：设计数据库表时，字段已经明确标了 `UNIQUE` 唯一约束，AI 生成的建表代码却又\"画蛇添足\"加了一个冗余索引——风格完全正确，语义纯属多余。还有更隐蔽的：依赖管理、工程规范层面的错误，单独看每一行都合理，组合在一起就是事故。我的\"删除变覆盖\"，正是这一类。",
          "",
          "做法：给 AI 代码设硬性门槛——必须过 Code Review + 测试才能合并；\"逻辑挺清晰的\"不是跳过 review 的理由，恰恰相反，越是看起来规整的代码越要逐行看。测试用例尽量全覆盖，重要的改动由资深开发者逐次审查。",
          "",
          "为什么：模式匹配器会生成\"风格正确、语义错误\"的代码。它不知道你的业务约束，它只知道好代码\"长这样\"。验证不是流程，是唯一能拦住它出错的手段。",
          "",
          "### 经验 3：让 AI 做它擅长的，小步迭代，别一次到位",
          "",
          "现象：我们实测下来的规律很清晰——相对独立的小任务（写一个函数、补一个模块、生成测试、写文档），AI 一两个小时就能出完整结果，质量可用；但把它丢进一个复杂系统的整体改造，翻车几乎是必然的：它看不到全局约束，局部\"没毛病\"的改动会在边界处连环出错。那次 7×24 上线翻车，就是\"一步到位\"的代价。",
          "",
          "做法：把任务拆分。独立小任务交给 AI，收益最高；复杂系统、生产环境、7×24 场景，自己把关。小步交付、每步验证，别让 AI 一次性生成几千行再回头看——步长越小，翻车点越早暴露，修复成本越低。",
          "",
          "为什么：AI 没有全局意识，它的\"正确\"是局部的、统计意义上的。复杂系统的正确性在边界（跨天、跨分区、跨服务、权限、异常路径），而这些恰恰是它最薄弱的地方。",
          "",
          "### 经验 4：补齐 AI 默认缺失的通用能力",
          "",
          "现象：AI 生成的代码有三样东西默认缺失：错误处理、结构化日志、权限控制。示例代码为了可读性会省略这些，AI 学到的就是\"省略版\"。在 7×24 场景里还要加一条：时间边界意识——跨天、跨时间分区的计算，它默认不会考虑。我们那次翻车，根因就在这条。",
          "",
          "做法：合并之前，显式检查四项：异常有没有统一处理？日志能不能定位问题？权限校验有没有漏？时间/边界场景有没有覆盖？每一项都要\"看见证据\"，不是\"看起来有\"。",
          "",
          "为什么：这些能力在训练数据里被省略，AI 不会主动补。你补上，它的产出才从\"演示代码\"变成\"可用代码\"。",
          "",
          "### 经验 5：责任不可剥夺——\"AI 告诉我这么做的\"不是借口",
          "",
          "现象：我们团队里一度流行一种免责话术：\"这是 AI 写的，不怪我。\"一旦这种文化形成，review 就形同虚设——反正出了事有 AI 背锅。我观察过：说出这句话的人，下次 review 时明显更放松，因为他觉得责任不在自己。",
          "",
          "做法：事故追责在人：谁提交、谁合并、谁负责。把\"责任在人\"写进团队规则，写进入职手册。AI 提交的代码，署名是人，责任也是人。",
          "",
          "为什么：AI 没有责任能力，也不会有学习压力。只有责任落在人身上，验证才会被认真执行，事故才不会一轮接一轮地重复。",
          "",
          "### 怎么开始用（落地清单，L1 执行层）",
          "",
          "1. 从独立小任务开始：补测试、写文档、生成单模块，先建立\"AI 产出 → 你验证\"的节奏；",
          "2. 给 AI 代码设门槛：review + 测试，一条都不少；",
          "3. 别追求\"100% AI 写代码\"——追求\"AI 帮你把简单的事做快，你把复杂的事想清楚\"；",
          "4. 心里那堵墙立起来：不拟人、不盲从、不甩锅。",
          "",
          "### 怎么迁移到你的场景（L3 深入层）",
          "",
          "- **个人项目**：你是唯一的 review 人，那就给自己加一条纪律——AI 代码合并前必须过一遍 `git diff`，重点看分支、边界、异常路径，而不是看格式；再用测试兜底。",
          "- **小团队**：把\"责任在人\"写进团队规则；给 AI 代码设\"资深者 review\"门槛；把高频翻车点沉淀成 review 检查清单（我们后来就是这样做的）。",
          "- **生产/7×24 系统**：AI 只做独立小任务，复杂改造自己把关；跨天、跨分区、权限、异常路径四项检查写进合并前 checklist；上线前强制灰度。",
          "- **如果你正在\"100% Vibe Coding\"**：不用全盘否定，但先把节奏改掉——小步、验证、责任在人，再谈效率。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/vibe-coding-consensus",
        "agent": "https://trove.xin/agent/problems/vibe-coding-consensus"
      }
    },
    {
      "id": "vite-chunkloaderror",
      "title": "前端发布后偶发白屏（ChunkLoadError / 版本错位）",
      "domain": "deploy",
      "domain_label": "构建与部署",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "vite-deploy",
        "nginx"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://juejin.cn/post/7678324462626471946",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "发布新版本后，**部分用户白屏**，刷新又偶尔恢复；控制台报 `ChunkLoadError: Loading chunk N failed`；动态 import 请求的带 hash 资源返回 404。你自己访问却是好的——因为你的缓存是新的。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "**版本错位**：浏览器缓存的旧入口 HTML 仍引用旧版本 hash chunk，而服务器新发布已经删掉了旧资源。用户触发动态 import 时，旧 chunk 404 → 模块加载失败 → 整页白屏。",
          "",
          "| 角色 | 状态 |",
          "|---|---|",
          "| 用户浏览器 | 缓存着旧版入口 HTML，指向旧 hash 资源 |",
          "| 服务器 | 已部署新版本，旧资源已删除 |",
          "| 结果 | 旧 chunk 404 → ChunkLoadError → 白屏 |",
          "",
          "Vite 构建的 chunk 文件名带内容 hash（实测：`feature-DP5yDwy7.js` → `feature-PcJGFbac.js`），版本一变 hash 就变，新旧 HTML 引用的资源完全不同。",
          "",
          "Vite 构建产物是「入口 HTML + 带内容 hash 的 chunk」：hash 由文件内容算出，内容变 hash 就变。浏览器先拿到入口 HTML，运行时再按 HTML 里的引用去加载 chunk——这是**两步取用**，中间隔着一段时间，而这段时间里服务器可能已经发布新版本并删除了旧 chunk。",
          "",
          "用户浏览器缓存的旧 HTML 指向旧 hash，服务器已没有该资源 → 动态 import 404 → ChunkLoadError。",
          "",
          "确定性推论：这不是代码 bug，是「客户端引用集」与「服务器资源集」的版本错位；修法三选一——部署时保留旧 chunk（不删 dist 旧文件/用带版本前缀的目录）、让入口 HTML 不缓存（`Cache-Control: no-cache`，chunk 仍可长缓存）、或升级时全量刷新（发布后提示刷新/自动 reload）。",
          "",
          "判断依据：Network 面板里失败请求的 URL 带旧 hash，而服务器 dist 里没有同名文件。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：发布顺序修正——先上资源、后切入口，旧资源保留至少一个发布周期",
          "# 1) 先把新版本 dist/ 全部上传（含新 hash 资源） 2) 最后再上传/切换 index.html",
          "```",
          "",
          "发布脚本里把 `index.html` 的覆盖放到**最后一步**，并且上传时保留旧资源目录。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 发布顺序：先资源后入口 — **必做**",
          "",
          "hash 静态资源先上线，入口 HTML 最后切换。这样新旧入口都能找到自己的资源。",
          "",
          "### 2. 旧资源保留过渡期 — **必做**",
          "",
          "至少保留一个发布周期的旧资源（连续发布时尤其重要），让还持有旧入口的用户平滑过渡。",
          "",
          "### 3. nginx fallback 不能吞静态资源 404 — **必做**",
          "",
          "`try_files` 先匹配真实文件，再回退 index.html，不能无差别 fallback：",
          "",
          "```bash",
          "location / {",
          "  try_files $uri $uri/ /index.html;   # 先真实文件，后回退",
          "}",
          "```",
          "",
          "### 4. CDN 与 Service Worker 遵循同一套版本一致性规则 — **必做**",
          "",
          "CDN 缓存策略和 SW 更新逻辑都要按\"资源先于入口\"设计，别让缓存层放大版本错位。"
        ],
        "怎么确认修好了": [
          "",
          "用旧版本入口模拟老用户访问（预期形态）：",
          "",
          "```text",
          "curl -H \"Cache-Control: no-cache\" http://你的域名/feature-PcJGFbac.js",
          "→ HTTP 200（新 hash 资源在）",
          "curl -I http://你的域名/index.html",
          "→ HTTP 200，引用资源列表与线上资源一致",
          "```",
          "",
          "发布后新旧页面均 HTTP 200（实测：保留旧资源后新旧页面均 200）；连续发布多轮不再出现 ChunkLoadError。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 单次发布 | 先资源后入口即可解决 |",
          "| 连续发布 | 旧资源至少保留一个发布周期，否则上一轮用户仍会白屏 |",
          "| nginx SPA fallback | 先 `try_files $uri` 再回退，别吞静态资源 404 |",
          "| CDN / Service Worker | 与服务器同一套版本一致性规则 |",
          "| 已白屏用户 | 强刷或重新打开即可恢复（缓存刷新后入口变新） |",
          ""
        ],
        "症状": [
          "",
          "发布后部分用户白屏、刷新又偶尔恢复；控制台报 ChunkLoadError: Loading chunk N failed；动态 import 请求的带 hash 资源返回 404。",
          ""
        ],
        "原因": [
          "",
          "版本错位：浏览器缓存的旧入口 HTML 仍引用旧版本 hash chunk，而新发布已删除旧资源 → 动态 import 404 → 模块加载失败 → 整页白屏。",
          ""
        ],
        "解法": [
          "",
          "1. 发布顺序：先资源后入口（hash 静态资源先上线，入口 HTML 最后切换）",
          "2. 旧资源保留过渡期（至少覆盖一个发布周期）",
          "3. nginx SPA fallback 不能吞静态资源 404（try_files 先匹配真实文件，再回退 index.html）",
          "4. CDN 与 Service Worker 遵循同一套版本一致性规则",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29）：vite 5.4.21 两次 build，chunk hash 变化（feature-DP5yDwy7.js → feature-PcJGFbac.js）；服务器只部署 v2 资源时旧 chunk HTTP 404（复现）；保留旧资源后新旧页面均 HTTP 200（解法成立）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/vite-chunkloaderror",
        "agent": "https://trove.xin/agent/problems/vite-chunkloaderror"
      }
    },
    {
      "id": "vite-history-fallback",
      "title": "Vue3 + Vite history 路由部署后刷新白屏（404）",
      "domain": "deploy",
      "domain_label": "构建与部署",
      "category": "build-deploy",
      "category_label": "构建与部署",
      "applies_to": [
        "vite-deploy",
        "nginx"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://juejin.cn/post/7485263262508138523",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "本地 `vite dev` 一切正常；打包部署到服务器后，访问首页正常、站内路由跳转也正常，但**手动刷新子路由页面（深链）时白屏**，控制台里深链请求返回 404。本地永远复现不了，问题只在部署后暴露。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "vue-router 用 `createWebHistory`（HTML5 History 模式）时，URL 不带 `#`。刷新时浏览器**直接向服务器请求子路由路径**（如 `/app/docs/setup`），服务器上没有这个物理文件，于是返回 404 → 白屏。`vite dev` 自带 history fallback，所以本地永远正常。",
          "",
          "| 场景 | 表现 |",
          "|---|---|",
          "| 站内跳转 | ✅ 前端路由接管，不发服务器请求 |",
          "| 刷新深链 | ❌ 浏览器直接请求 `/app/docs/setup`，服务器无此文件 → 404 |",
          "| 首页直接访问 | ✅ 物理文件存在 |",
          "",
          "HTML5 History 模式（`createWebHistory`）把路由状态放进 URL 路径，**刷新时浏览器不经过前端路由，直接向服务器发完整路径的请求**——服务器按物理文件找 `/app/docs/setup`，找不到就 404。站内跳转不触发服务器请求（前端 router 拦截），所以「站内导航正常、一刷新就白屏」是这类问题的标志性特征。",
          "",
          "`vite dev` 内置 history fallback（所有路径回退到 index.html），本地永远正常，部署后消失——这是「开发环境与生产环境静态服务器行为不同」的典型。",
          "",
          "确定性推论：生产服务器必须配「未知路径回退到 index.html」：nginx `try_files $uri $uri/ /index.html;`、静态托管平台开 SPA fallback、或 Node 服务器 `app.get('*', ...)` 返回 index.html；配好后刷新深链返回 200 而不是 404 即修复。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：nginx 核心修复——不存在的路径回退到 SPA 入口，静态资源 404 单独暴露",
          "# location /app/ { alias /path/to/dist/; try_files $uri $uri/ /app/index.html; }",
          "# location /app/assets/ { alias /path/to/dist/assets/; try_files $uri =404; }",
          "```",
          "",
          "把这两段 location 按你的部署目录改好，`nginx -t && nginx -s reload`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 排查第一步：base 一致性 — **排查**",
          "",
          "`vite.config` 的 `base`、`createWebHistory(base)`、服务器部署目录三者必须**完全一致**（尤其结尾 `/`）。部分资源 404 / 图标丢失通常是 base 不一致。",
          "",
          "### 2. nginx history fallback（核心修复） — **必做**",
          "",
          "```nginx",
          "location /app/ {",
          "  alias /path/to/dist/;",
          "  try_files $uri $uri/ /app/index.html;",
          "}",
          "```",
          "",
          "### 3. 静态资源 404 不能被 fallback 吞掉 — **必做**",
          "",
          "无差别 fallback 会把缺失的 JS/CSS 也返回 200 + HTML（动态 import 失败时更难排查，与 ChunkLoadError 互相印证）。给 assets 单独一个 location：",
          "",
          "```nginx",
          "location /app/assets/ {",
          "  alias /path/to/dist/assets/;",
          "  try_files $uri =404;",
          "}",
          "```",
          "",
          "### 4. Apache 等价写法 — **必做**",
          "",
          "`.htaccess`：`RewriteEngine On` + 文件/目录不存在时 rewrite 到 `/app/index.html`（逻辑同第 2 步）。"
        ],
        "怎么确认修好了": [
          "",
          "curl 断言三类路径（预期输出）：",
          "",
          "```text",
          "curl -I http://你的域名/app/docs/setup",
          "→ HTTP 200（深链回退到 HTML 入口）",
          "curl -I http://你的域名/app/assets/main.js",
          "→ HTTP 200（真实资源）",
          "curl -I http://你的域名/app/assets/missing.js",
          "→ HTTP 404（缺失资源正确暴露，不被吞）",
          "```",
          "",
          "浏览器刷新任意深链不再白屏（实测：修正后深链 200、真实资源 200、缺失资源 404）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| nginx alias 部署 | 深链回退 + assets 独立 location（实测修正方案） |",
          "| nginx root 部署 | 同样 `try_files $uri $uri/ /index.html`，注意 base 路径 |",
          "| Apache | `.htaccess` rewrite，逻辑相同 |",
          "| base 不一致 | 部分资源 404 / 图标丢失，先查 base 再查 fallback |",
          "| 无差别 fallback | 缺失 JS 也返回 200 HTML，排查更难（实测踩坑） |",
          ""
        ],
        "症状": [
          "",
          "本地 `vite dev` 一切正常；打包部署到服务器后，直接访问首页正常、站内路由跳转正常，但**手动刷新子路由页面（深链）时白屏**，控制台里深链请求返回 404。",
          ""
        ],
        "原因": [
          "",
          "vue-router 使用 `createWebHistory`（HTML5 History 模式）时，URL 不带 `#`。刷新时浏览器直接向服务器请求子路由路径（如 `/app/docs/setup`），服务器上没有这个物理文件，于是返回 404 → 白屏。`vite dev` 自带 history fallback，所以本地永远复现不了，问题只在部署后暴露。",
          ""
        ],
        "解法": [
          "",
          "1. **base 一致性（排查第一步）**：`vite.config` 的 `base`、`createWebHistory(base)`、服务器部署目录三者必须完全一致（尤其结尾 `/`）。部分资源 404 / 图标丢失通常是 base 不一致。",
          "2. **nginx history fallback（核心修复）**：把不存在的路径回退到 SPA 入口：",
          "",
          "   ```nginx",
          "   location /app/ {",
          "     alias /path/to/dist/;",
          "     try_files $uri $uri/ /app/index.html;",
          "   }",
          "   ```",
          "",
          "3. **静态资源 404 不能被 fallback 吞掉（实测修正）**：无差别 fallback 会把缺失的 JS/CSS 也返回 200 + HTML，动态 import 失败时更难排查（与 ChunkLoadError 条目互相印证）。给 assets 单独一个 location，让真实 404 暴露：",
          "",
          "   ```nginx",
          "   location /app/assets/ {",
          "     alias /path/to/dist/assets/;",
          "     try_files $uri =404;",
          "   }",
          "   ```",
          "",
          "4. **Apache 等价写法**：`RewriteEngine On` + 文件/目录不存在时 rewrite 到 `/app/index.html`（`.htaccess`，逻辑同 2）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29）：nginx 1.28.3 + 最小 SPA fixture（`index.html` + `assets/`），`alias` 部署 + `curl` 断言。",
          "- 复现：无 fallback 时 `GET /app/docs/setup` → 404（刷新白屏根因成立），`/app/index.html` → 200。",
          "- 文章解法：`try_files ... /app/index.html` 后深链 → 200（HTML 入口）；但 `GET /app/assets/missing.js` → 200 HTML —— 静态 404 被吞（文章未提的坑）。",
          "- 修正后：深链 200、真实资源 200、缺失资源 404（正确暴露）。",
          "- `实测复现日志（2026-08-29）`"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/vite-history-fallback",
        "agent": "https://trove.xin/agent/problems/vite-history-fallback"
      }
    },
    {
      "id": "wsl-default-user-root-after-import",
      "title": "wsl --import 迁移/重装后默认用户变成 root，whoami 变了、权限全乱",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "微软官方文档机制核对 + 本机对照（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "`wsl --export` 备份、`wsl --import` 迁移/重装发行版之后，一进 WSL `whoami` 变成了 `root`：家目录、ssh 密钥、文件属主全乱套。更迷惑的是跑 `<发行版> config --default-user 你的用户名` 报错或不生效——**导入的发行版没有可执行启动器，那条命令对它们无效**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "WSL 发行版的启动用户由两个来源决定：**Store 版发行版的启动器默认用户**（`<发行版> config --default-user` 改的就是它），以及 **`/etc/wsl.conf` 的 `[user] default=`**。",
          "",
          "`wsl --import` 导入的发行版**没有可执行启动器**，所以 `config --default-user` 对它们不生效——官方文档明确说了这一点，唯一途径是 wsl.conf。",
          "",
          "export 生成的 tar 会原样保留发行版里的 `/etc/wsl.conf`：如果原发行版从来没写过 `[user] default=`，导入后就没有任何配置指定普通用户，WSL 按默认值以 **root** 启动。这解释了「迁移前好好的、迁移后变 root」：问题不在迁移，而在原配置本来就缺 `[user]` 段。",
          "",
          "确定性推论：导入后 `whoami` = root，先 `cat /etc/wsl.conf` 看有没有 `[user]` 段——没有就直接补上再重启发行版；`config --default-user` 只对 Store 安装的发行版有效，导入版用它白折腾。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：给导入的发行版指定默认用户（改完必须重启发行版才生效）",
          "echo -e \"[user]\\ndefault=你的用户名\" >> /etc/wsl.conf",
          "```",
          "",
          "然后在 Windows 侧终止该发行版并重进：",
          "",
          "```powershell",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认现状 — **必做**",
          "",
          "```bash",
          "whoami          # root = 缺 [user] 配置",
          "cat /etc/wsl.conf",
          "```",
          "",
          "### 2. 写入 [user] 段 — **必做**",
          "",
          "```bash",
          "# 用户名必须已存在（useradd 建过或用过的）",
          "echo -e \"[user]\\ndefault=你的用户名\" >> /etc/wsl.conf",
          "```",
          "",
          "已有 `[user]` 段就只改 `default=` 那一行，别重复追加段头。",
          "",
          "### 3. 重启发行版生效 — **必做**",
          "",
          "```bash",
          "# Windows 侧执行（不是 WSL 里）",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          "",
          "wsl.conf 只在发行版启动时读取，不重启不生效。",
          "",
          "### 4. Store 版发行版（非导入） — **按场景**",
          "",
          "普通安装的 Ubuntu 想改默认用户，二选一：`ubuntu config --default-user 用户名`（PowerShell/CMD）或同样写 wsl.conf。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "重进后 `whoami` 输出目标用户名；`wsl --user root` 仍可临时切 root（`--user` 是单次覆盖，不影响默认）；家目录/ssh 权限恢复正常。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 导入的发行版（wsl --import） | `config --default-user` 无效，只能 wsl.conf（官方文档明确） |",
          "| Store 安装的发行版 | `ubuntu config --default-user <用户>` 有效 |",
          "| 单次切换 | `wsl -d <发行版> -u <用户>` 只对本次有效 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ whoami",
          "root",
          "$ ubuntu config --default-user myuser   # 导入版：报错或不生效",
          "```",
          "",
          "特征：迁移/导入前是普通用户、导入后变 root；`/etc/wsl.conf` 没有 `[user]` 段；`config --default-user` 对导入版无效。",
          ""
        ],
        "原因": [
          "",
          "WSL 启动用户由启动器默认用户或 `/etc/wsl.conf [user] default=` 决定。`wsl --import` 导入的发行版没有可执行启动器，`config --default-user` 不生效；export 保留原 wsl.conf，原配置缺 `[user]` 段时导入后默认 root。",
          ""
        ],
        "解法": [
          "",
          "1. `whoami` + `cat /etc/wsl.conf` 确认缺 `[user]` 段；",
          "2. `echo -e \"[user]\\ndefault=你的用户名\" >> /etc/wsl.conf`（用户名须已存在）；",
          "3. Windows 侧 `wsl --terminate <发行版名>` 重启发行版；",
          "4. 重进验证 `whoami`。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制核对：微软官方文档（basic-commands 的 config --default-user 警告 + wsl-config 的 [user] default=）；",
          "- 本机对照（2026-08-31）：定制预构建版 `whoami` = root 且 `/etc/wsl.conf` 无 `[user]` 段，与机制结论一致；未修改配置复现故障（避免影响工作环境）；",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [WSL 注册发行版失败错误码族](wsl-register-failed.html)（同一域：WSL 发行版生命周期问题）",
          "  - [WSL systemd 起不来](wsl-systemd-not-running.html)（wsl.conf 的另一常见坑：改完不重启不生效）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-default-user-root-after-import",
        "agent": "https://trove.xin/agent/problems/wsl-default-user-root-after-import"
      }
    },
    {
      "id": "wsl-dns-resolution-failure",
      "title": "WSL 里报 Temporary failure in name resolution / 连不上网，VPN 一连上 WSL 就断网",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "https://learn.microsoft.com/windows/wsl/troubleshooting",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "WSL 里 `curl` / `git clone` / `apt update` 报 `Temporary failure in name resolution`（或 `无法解析主机名`），但 Windows 侧上网正常；典型场景：**VPN 一连上，WSL 就断网**；或每次重启 WSL 后 `/etc/resolv.conf` 被 Windows 重写成失效地址。",
          "",
          "诊断口诀：先分清是「解析不了名字」还是「路由不通」——`ping 8.8.8.8` 通但 `ping github.com` 报解析失败 = DNS 问题，本文管的就是这个。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "先说结论：**这不是 WSL 坏了，而是「DNS 上游不可达」**。整条解析链路只会断在三个点：resolv.conf 被覆盖、stub 上游不可达、NAT 通道被 VPN 破坏。下面拆开讲，方便你对号入座。",
          "",
          "**链路：DNS 是 Windows 侧「抄」给 WSL 的。**",
          "",
          "WSL2 网络是一张 NAT 虚拟交换机，WSL 没有自己的物理网卡，DNS 只能依赖 Windows 侧下发。`generateResolvConf=true` 时，每次启动 WSL 都会重写 `/etc/resolv.conf`。",
          "",
          "systemd 发行版（Ubuntu 22.04+）的 `/etc/resolv.conf` 实际指向 stub `127.0.0.53`——stub 自己不解析，只把查询转发给上游。上游来自 Windows 侧 DNS，用 `resolvectl status` 能直接看到。",
          "",
          "**VPN 的坑：上游只在隧道内可达。**",
          "",
          "VPN 一连上，Windows 就把 VPN 的 DNS 抄给 WSL。这个 DNS 服务器只在隧道内可达，stub 在隧道外转发必然失败——于是报 `Temporary failure in name resolution`。",
          "",
          "**三个断点，对应三类修法：**",
          "",
          "1. resolv.conf 被覆盖/写错 → 固定它：`generateResolvConf=false` + 手动 nameserver；",
          "2. stub 上游不可达（典型：VPN DNS 在隧道内）→ 换上游：`/etc/systemd/resolved.conf` 的 `DNS=` 或 `resolvectl`；",
          "3. NAT 转发本身被 VPN 破坏 → 绕开 NAT 的 DNS 通道：`.wslconfig` 开 `dnsTunneling=true`（DNS 查询走 Windows 侧处理）或 `networkingMode=mirrored`（WSL 与 Windows 共享网络栈）。",
          "",
          "注意：`generateResolvConf=false` 只阻止「被覆盖」，不保证「上游可达」——它只治断点 1，治不了断点 2 和 3。",
          "",
          "**本机实测（2026-08-31）**：resolv.conf = stub（127.0.0.53）、上游 = 223.5.5.5/119.29.29.29（来自 resolved.conf 配置）、`getent hosts github.com` 正常。",
          "",
          "把 resolv.conf 指向不可达地址（203.0.113.1）后，`getaddrinfo` 报 `[Errno -3] Temporary failure in name resolution`；恢复后解析正常——「nameserver 指向不可达地址 = 这个报错」的链路成立。",
          "",
          "**确定性推论**：先 `resolvectl status` 看上游。上游不可达 → 换可达 DNS；上游正常还断网 → 是路由/通道问题（用 `ping 8.8.8.8` 验证），往 dnsTunneling / mirrored 方向查。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 立即恢复（systemd 发行版，Ubuntu 22.04+）：给 stub 换可达上游并生效",
          "sudo sed -i 's/^#*DNS=.*/DNS=223.5.5.5 119.29.29.29/' /etc/systemd/resolved.conf",
          "sudo systemctl restart systemd-resolved",
          "getent hosts github.com   # 返回 IP = 修好了",
          "",
          "# 非 systemd 发行版：直接写 resolv.conf（重启后可能被覆盖，见「一步步来」治本）",
          "sudo sh -c 'echo \"nameserver 223.5.5.5\" > /etc/resolv.conf'",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 分清 DNS 还是路由 — **必做**（30 秒定位）",
          "",
          "```bash",
          "ping -c1 223.5.5.5        # 通 = 路由没问题；不通 = 网络通道问题（VPN/虚拟交换机）",
          "getent hosts github.com   # 通 = DNS 没问题；报 Temporary failure = DNS 问题，往下走",
          "```",
          "",
          "### 2. 看当前解析链路 — **必做**（知道断在哪一层）",
          "",
          "```bash",
          "cat /etc/resolv.conf      # stub=127.0.0.53（systemd）或直接 nameserver（非 systemd）",
          "resolvectl status         # systemd：看 Current DNS Server / DNS Servers（上游从哪来）",
          "```",
          "",
          "上游是 127.0.0.53 且系统解析失败 → 换上游（快速修复段）；resolv.conf 直接是失效地址 → 修它。",
          "",
          "### 3. 固定 resolv.conf，防重启被覆盖 — **治本**（generateResolvConf 开关）",
          "",
          "```ini",
          "# /etc/wsl.conf",
          "[network]",
          "generateResolvConf = false",
          "```",
          "",
          "然后 `wsl --shutdown` 重开。注意：systemd 发行版推荐保留 stub、改 `resolved.conf` 上游（快速修复段），**不要**手动写 nameserver 覆盖 stub（会与 systemd-resolved 打架）；非 systemd 发行版才手动写 resolv.conf。",
          "",
          "### 4. VPN 场景：换通道 — **按场景**（VPN 一连上 WSL 就断）",
          "",
          "VPN 的 DNS 只在隧道内 → 换公共 DNS 无效时，改通道：",
          "",
          "```ini",
          "# %USERPROFILE%\\.wslconfig（Windows 侧）",
          "[wsl2]",
          "dnsTunneling=true        # DNS 查询走 Windows 侧（官方推荐 VPN 场景先试这个）",
          "# networkingMode=mirrored  # 更彻底：WSL 与 Windows 共享网络栈（二选一）",
          "```",
          "",
          "`wsl --shutdown` 后重开。官方 troubleshooting 还给了手动兜底：记下 VPN 的 DNS 服务器，写进 resolv.conf。",
          "",
          "### 5. 其它断点 — **排查**（少见但真实）",
          "",
          "IPv6 被禁用（注册表 DisabledComponents）→ WSL 网络可能异常；`.wslconfig` 配置语法错误 → `wsl --shutdown` 后整段不生效，先 `wsl --version` 确认版本支持（dnsTunneling 需 WSL 2.2+）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "getent hosts github.com && curl -I --max-time 8 https://github.com | head -1",
          "# getent 返回 IP、curl 返回 HTTP/2 200 = 全链路通",
          "wsl --shutdown && wsl -d Ubuntu   # 重启后仍通 = 治本成功（防覆盖配置生效）",
          "```",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Ubuntu 22.04+（systemd） | 走 stub，改 resolved.conf 上游；不要手动覆盖 resolv.conf |",
          "| 老发行版（非 systemd） | 直接写 resolv.conf + generateResolvConf=false |",
          "| VPN 场景 | 换公共 DNS 无效 → dnsTunneling=true / mirrored（.wslconfig，需 WSL 2.2+） |",
          "| generateResolvConf=false 已设 | 阻止覆盖，但上游不可达照样断——查 resolvectl status |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ curl -I https://github.com",
          "curl: (6) Could not resolve host: github.com",
          "$ apt update",
          "Temporary failure resolving 'archive.ubuntu.com'",
          "$ getent hosts github.com",
          "（无输出，exit=2）",
          "```",
          "",
          "特征：Windows 侧上网正常；`ping 8.8.8.8` 通但域名全解析失败；VPN 一连上就断；重启 WSL 后复发（被覆盖）。",
          ""
        ],
        "原因": [
          "",
          "WSL2 NAT 模式下 DNS 由 Windows 侧下发：`generateResolvConf=true` 每次启动重写 `/etc/resolv.conf`；systemd 发行版走 stub（127.0.0.53）再转发上游；VPN 的 DNS 在隧道内不可达 → 转发失败 → `Temporary failure in name resolution`。三个断点：resolv.conf 被写错/覆盖、stub 上游不可达、NAT 通道被 VPN 破坏。",
          ""
        ],
        "解法": [
          "",
          "1. 定位：`ping 223.5.5.5`（路由）vs `getent hosts github.com`（DNS）；2. systemd 发行版：`/etc/systemd/resolved.conf` 设 `DNS=223.5.5.5 119.29.29.29` + `systemctl restart systemd-resolved`；3. 防覆盖：`/etc/wsl.conf` `generateResolvConf=false`；4. VPN 场景：`.wslconfig` `dnsTunneling=true` 或 `networkingMode=mirrored`；5. 验证：`getent hosts github.com` + `curl -I`，重启 WSL 后仍通。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机实测（2026-08-31，定制预构建版 WSL，DNS 机制与 WSL 版本无关）：实测复现日志（2026-08-31）",
          "  - 基线：resolv.conf=stub（127.0.0.53）、resolved.conf 上游=223.5.5.5/119.29.29.29、`getent hosts github.com` → 20.205.243.166；",
          "  - 复现：resolv.conf 指向不可达地址（203.0.113.1）→ `getaddrinfo` 报 `[Errno -3] Temporary failure in name resolution`、`getent` exit=2；UDP 探测 127.0.0.53 正常应答 / 203.0.113.1 超时；",
          "  - 恢复：还原 resolv.conf 后解析恢复正常（trap 兜底 + 显式恢复，无残留）；",
          "  - 治本配置与本文推荐一致：wsl.conf `generateResolvConf=false` + resolved.conf 公共上游。",
          "- 未实测（如实标注）：VPN 场景（本机无 VPN）、dnsTunneling/mirrored 切换（会动网络栈）。",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- [只有个别网站打不开（其他网站正常）——分层定位](site-open-fail-others-ok.html)（相邻：单域名失败，同一套分层法）",
          "- **相关（报错相近，根因不同）**：",
          "  - 本页是「WSL 网络/DNS」canonical；相邻 WSL 问题：wsl-register-failed（启动就失败）、wsl-no-space-left（启动后写不进盘）、wsl-pipe-encoding（PowerShell 调 wsl 乱码）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-dns-resolution-failure",
        "agent": "https://trove.xin/agent/problems/wsl-dns-resolution-failure"
      }
    },
    {
      "id": "wsl-interop-disabled",
      "title": "WSL 里运行 notepad.exe / cmd.exe 报 command not found（interop 失效）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "微软官方文档机制核对 + 本机对照（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "在 WSL 里 `notepad.exe` / `cmd.exe` / `explorer.exe` 全部报 `command not found`，`which cmd.exe` 也找不到；Windows 侧的程序一个都调不动。",
          "",
          "**WSL 的 interop（Windows/Linux 互操作）被关掉了**——多半是 `/etc/wsl.conf` 里写了 `[interop] enabled=false` 或 `appendWindowsPath=false`（照着某些「纯净化/安全加固」教程抄的）。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "interop 是 WSL 的互操作机制（默认开启）：把 Windows 可执行文件通过 binfmt 注册进 Linux，让你在 WSL 里直接运行 `.exe`，并把 Windows PATH 追加进 Linux PATH。",
          "",
          "`/etc/wsl.conf` 的 `[interop]` 段两个开关：`enabled=false` **直接关掉运行 Windows 进程的能力**（binfmt 注册被移除）；`appendWindowsPath=false` **不再把 Windows PATH 追加进来**（`which cmd.exe` 找不到）。",
          "",
          "`enabled` 管的是「能不能执行」：WSL 启动时根据该开关注册/不注册 binfmt 处理器，关掉后任何 `.exe` 都是 `command not found`。",
          "",
          "`appendWindowsPath` 管的是「找不找得到」：WSL 默认把 Windows 的 PATH 追加到 Linux PATH 末尾，关掉后 `/mnt/c/Windows/system32/` 不在 PATH 里，就算 interop 开着也直接输路径才能运行。",
          "",
          "两个开关独立，症状不同：`enabled=false` = 直接运行报 command not found；`appendWindowsPath=false` = which 找不到但 `/mnt/c/.../cmd.exe` 全路径能跑。确定性推论：先 `cat /etc/wsl.conf` 看 `[interop]` 段，再 `which cmd.exe` 分流——两个都查过再动手。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# WSL 内：打开两个开关（没有该段就整段追加）",
          "echo -e \"[interop]\\nenabled=true\\nappendWindowsPath=true\" >> /etc/wsl.conf",
          "```",
          "",
          "```powershell",
          "# Windows 侧：重启发行版生效",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 看 wsl.conf 有没有 [interop] 段 — **必做**",
          "",
          "```bash",
          "cat /etc/wsl.conf",
          "```",
          "",
          "### 2. 按症状分流 — **按诊断结果**",
          "",
          "- 有 `enabled=false` → 改成 `enabled=true`；",
          "- 有 `appendWindowsPath=false` → 改成 `appendWindowsPath=true`；",
          "- 两行都有 → 全改回来。",
          "",
          "### 3. 重启发行版 — **必做**",
          "",
          "```bash",
          "# Windows 侧",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          "",
          "### 4. 验证 — **必做**",
          "",
          "```bash",
          "which cmd.exe                # 能找到 = PATH 已追加",
          "cmd.exe /c echo interop-ok   # 能执行 = binfmt 注册恢复",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`which cmd.exe` 命中 `/mnt/c/Windows/system32/cmd.exe`；`cmd.exe /c echo ok` 正常执行（控制台中文乱码是 UTF-16 编码显示问题，不影响执行成功）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 开关 | 症状 |",
          "|---|---|",
          "| `enabled=false` | 直接运行 `.exe` 报 command not found（binfmt 没注册） |",
          "| `appendWindowsPath=false` | `which` 找不到，但全路径 `/mnt/c/Windows/system32/cmd.exe` 能跑 |",
          "| 两个都关 | 两种症状叠加 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ notepad.exe",
          "-bash: notepad.exe: command not found",
          "$ which cmd.exe",
          "（无输出）",
          "```",
          "",
          "特征：WSL 里 Windows 程序集体消失；`/etc/wsl.conf` 有 `[interop]` 段且值为 false；改完没重启发行版也会「看起来没生效」。",
          ""
        ],
        "原因": [
          "",
          "interop 默认开启，靠启动时注册 binfmt（enabled）与追加 Windows PATH（appendWindowsPath）实现。wsl.conf 把两个开关设为 false 后，Windows 进程不可执行且 PATH 里找不到。改 wsl.conf 必须重启发行版生效。",
          ""
        ],
        "解法": [
          "",
          "1. `cat /etc/wsl.conf` 找 `[interop]` 段；",
          "2. `enabled=true` + `appendWindowsPath=true`（没有该段就整段追加）；",
          "3. Windows 侧 `wsl --terminate <发行版名>` 重启；",
          "4. `which cmd.exe` + 运行验证。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制核对：微软官方文档（learn.microsoft.com/windows/wsl/wsl-config 的 [interop] 段）；",
          "- 本机对照（2026-08-31）：定制预构建版未配置 [interop] 段（默认全开）——`which cmd.exe` 命中、`cmd.exe /c echo` 可执行；未做故障复现（避免影响工作环境）；",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [WSL systemd 起不来](wsl-systemd-not-running.html)（同为 wsl.conf 配置坑：改完必须重启发行版）",
          "  - [PowerShell 调 wsl 输出乱码](wsl-pipe-encoding.html)（WSL 互操作的编码面问题）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-interop-disabled",
        "agent": "https://trove.xin/agent/problems/wsl-interop-disabled"
      }
    },
    {
      "id": "wsl-no-space-left",
      "title": "WSL 里报 No space left on device，但 df 显示还有大量空间",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/microsoft/WSL/issues/1665",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "WSL 里解压/编译/写入大文件时报 `No space left on device`，但 `df -h /` 明明显示还有几百 G 可用（`/dev/sdd 1007G 190G 766G 20% /`）。你第一反应是\"我明明有空间\"——这是 WSL2 最经典的悖论：**df 显示的是虚拟容量，不是宿主盘上 vhdx 文件的真实大小**。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "WSL2 的 ext4 文件系统建在**动态扩张的 vhdx 虚拟磁盘**上：",
          "",
          "- `df` 显示虚拟容量（默认最大 1TB 级）；",
          "- 写入需要 vhdx 物理扩张时，要求**宿主盘（默认 C 盘）有剩余空间**；",
          "- 宿主盘满了 → 扩张失败 → 内核返回 ENOSPC（No space left on device）。",
          "",
          "于是出现「df 显示有空间、实际写不进去」的悖论。经典 issue：[microsoft/WSL#1665](https://github.com/microsoft/WSL/issues/1665)（随机 ENOSPC）、#4699（372 comments：删了文件 vhdx 也不还给宿主）。",
          "",
          "> 实测环境：WSL Ubuntu，vhdx 在 D:\\WSL\\Ubuntu，宿主 D 盘仅剩 9GB，2026-08-29。",
          "",
          "WSL2 的 ext4 文件系统住在一个 **vhdx 虚拟磁盘**里：`df` 显示的是虚拟磁盘的容量上限（默认 1TB 级），而真实占用来自 vhdx 文件的物理扩张。写文件需要扩张时，内核向宿主盘（默认 C 盘）申请空间——宿主盘满了，扩张失败，内核就把这次写入判为 ENOSPC。于是出现「虚拟容量显示有空间、物理宿主没空间」的双层账本悖论。",
          "",
          "确定性推论：`df -h /` 有空间但写不进 = 查宿主盘（`df -h /mnt/c`）和 vhdx 实际大小（`ls -lh *.vhdx`）；删了文件 vhdx 也不缩小是正常行为（稀疏文件只缩不自动回收），需要 `wsl --shutdown` 后 `Optimize-VHD`/`diskpart compact` 手动压缩；",
          "",
          "长期解法是把发行版迁到空间大的盘（`wsl --export`/`--import`）而不是在快满的盘上硬撑。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：确认 vhdx 位置 + 宿主盘剩余空间（PowerShell）",
          "Get-ChildItem \"$env:LOCALAPPDATA\\Packages\\*\\LocalState\\ext4.vhdx\" | Select FullName, @{n='GB';e={[math]::Round($_.Length/1GB,1)}}",
          "Get-PSDrive C | Select Used, Free",
          "```",
          "",
          "宿主盘清出空间后 WSL 立即恢复写入。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 定位 vhdx 在哪个盘 — **排查**",
          "",
          "PowerShell：",
          "",
          "```powershell",
          "Get-ChildItem \"$env:LOCALAPPDATA\\Packages\\*\\LocalState\\ext4.vhdx\" | Select FullName",
          "```",
          "",
          "或注册表 `HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\{GUID}` 的 BasePath。",
          "",
          "### 2. 看宿主盘剩余 — **必做**",
          "",
          "```powershell",
          "Get-PSDrive D",
          "```",
          "",
          "宿主盘 Free 很小（本机实测 D 盘剩 9GB 时 WSL 内写 2.7G 就 ENOSPC）→ 清宿主盘空间（回收站/大文件/其他发行版 vhdx）即可立即恢复。",
          "",
          "### 3. 让 vhdx 归还空间（治本，删了文件不缩盘） — **必做**",
          "",
          "- 新 WSL（2.4.4+）：`wsl --shutdown` 后 `wsl --manage <发行版> --set-sparse true`，删除文件自动还空间；",
          "- 老版本：`wsl --shutdown` + 管理员 diskpart `compact vdisk`（或 Hyper-V `Optimize-VHD`）。",
          "",
          "### 4. 换大盘 — **必做**",
          "",
          "```bash",
          "wsl --export <发行版> <文件.tar>",
          "```",
          "",
          "新位置 `wsl --import <发行版> <目录> <文件.tar>`（import 后默认 root/默认发行版要重设，`wsl --set-default`）。",
          "",
          "### 5. 防患 — **必做**",
          "",
          "docker-desktop 等发行版也各自占 vhdx；大文件放 `/mnt/c`（直通宿主盘，不占 vhdx）。"
        ],
        "怎么确认修好了": [
          "",
          "```bash",
          "dd if=/dev/zero of=/tmp/x.bin bs=1M count=5120 conv=fsync && rm /tmp/x.bin",
          "```",
          "",
          "不再报 `No space left on device` 即成功。实测：宿主盘清理前写 5GB 在 ~2.7GB 处报错、df 仍显示 766G 可用；清理后恢复。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 默认安装（C 盘） | vhdx 在 `%LOCALAPPDATA%\\Packages\\*\\LocalState\\`，清 C 盘 |",
          "| 迁移到 D 盘 | 清 D 盘；本机实测 D 盘剩 9GB 即触发 |",
          "| 删了文件空间没回来 | vhdx 不自动缩，用 --set-sparse / diskpart compact |",
          "| docker-desktop | 其 vhdx 单独占空间，同样处理 |",
          ""
        ],
        "症状": [
          "",
          "WSL 里解压/编译/写入大文件时报 `No space left on device`，但：",
          "",
          "```",
          "$ df -h /",
          "/dev/sdd  1007G  190G  766G  20% /     # 明明还有 766G 可用",
          "$ dd if=/dev/zero of=/tmp/x.bin bs=1M count=5120 conv=fsync",
          "dd: IO error: No space left on device   # 写 ~2.7G 就失败",
          "```",
          "",
          "特征：df 显示可用空间充足（甚至 80% 空闲）；失败点随机（解压到不同文件）；",
          "用户第一反应是\"我明明有空间\"（[microsoft/WSL#1665](https://github.com/microsoft/WSL/issues/1665) 原话 I do have enough space on my device）。",
          ""
        ],
        "原因": [
          "",
          "WSL2 的 ext4 文件系统建在**动态扩张的 vhdx 虚拟磁盘**上：`df` 显示的是虚拟容量（默认最大 1TB 级），",
          "不是宿主盘上 vhdx 文件的实际大小。当 WSL 内写入需要 vhdx 物理扩张，而 vhdx 所在的宿主盘",
          "（默认 C 盘，或迁移后的盘）剩余空间不足时，扩张失败 → 内核返回 ENOSPC。",
          "于是出现「df 显示有空间、实际写不进去」的悖论。相关经典 issue：",
          "[microsoft/WSL#1665](https://github.com/microsoft/WSL/issues/1665)（随机 ENOSPC）、#4699（372 comments：删了文件 vhdx 也不还给宿主）。",
          ""
        ],
        "解法": [
          "",
          "1. **先定位 vhdx 在哪个盘**：注册表 `HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\{GUID}` 的 BasePath，",
          "   或 PowerShell `Get-ChildItem \"$env:LOCALAPPDATA\\Packages\\*\\LocalState\\ext4.vhdx\"`；",
          "2. **看宿主盘剩余**：`Get-PSDrive` 检查 vhdx 所在盘 Free——本机实测 D 盘只剩 9GB 时 WSL 内写 2.7G 就 ENOSPC。",
          "   清宿主盘空间（回收站/大文件/其他发行版 vhdx）即可立即恢复；",
          "3. **让 vhdx 归还空间（治本，删了文件不缩盘）**：",
          "   - 新 WSL（2.4.4+）：`wsl --shutdown` 后 `wsl --manage <发行版> --set-sparse true`，vhdx 稀疏化，删除文件自动还空间；",
          "   - 老版本：`wsl --shutdown` + 管理员 diskpart `compact vdisk`（或 Hyper-V `Optimize-VHD`）；",
          "4. **换大盘**：`wsl --export <发行版> <文件.tar>` → 新位置 `wsl --import <发行版> <目录> <文件.tar>`（注意 import 后默认 root 用户/默认发行版要重设，可 `wsl --set-default`）；",
          "5. **防患**：docker-desktop 等发行版也各自占 vhdx；大文件放 /mnt/c（直通宿主盘，不占 vhdx），",
          "   或把 vhdx 容量上限调小（`wsl --resize` 部分版本支持）。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，vhdx 在 D:\\WSL\\Ubuntu，宿主 D 盘仅剩 9GB）：",
          "  - 基线：df 显示 766G 可用 / vhdx 文件 194.90GB / D 盘 Free 9.0GB；",
          "  - 复现：dd 写 5GB 在 ~2.7GB 处报 `IO error: No space left on device`，随后 `df -h /` 仍显示 766G 可用；",
          "  - 机制核对：D 盘 Free 9GB（vhdx 无物理空间可扩），df 是虚拟容量——与 #1665 症状同款；",
          "  - 清理：删除部分文件后 df 恢复，D 盘 Free 不变、vhdx 大小不变（删除不还空间，即 #4699 诉求点）；",
          "  - 未实测（需管理员/重启，如实标注）：diskpart compact、--set-sparse、--export/--import 迁移。",
          "- 实测复现日志（2026-08-29）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **同根（同一根因）**：",
          "  - [docker pull 失败/镜像加速](docker-hub-pull-fail.html)（同根：pull 拉取/解压阶段报 `no space left on device`，真相常是 WSL 宿主盘满——先查 vhdx 所在盘剩余空间）",
          "  - [Docker daemon 连不上](docker-daemon-connect.html)（同根：Docker 侧连接/环境类，先排除 daemon 状态再查磁盘）",
          "  - [ollama pull 卡住/超时](ollama-pull-timeout.html)（同根：模型下载/导入阶段 ENOSPC，模型 4~70GB 先查盘）",
          "  - [pip 安装超时/慢](pip-mirror-source.html)（同根：下载/安装阶段 ENOSPC）",
          "  - [npm install 慢/挂起](npm-mirror-registry.html)（同根：缓存写入阶段 ENOSPC）",
          "  - [Go 模块拉取慢/超时](go-proxy-cn.html)（同根：下载/构建阶段 ENOSPC）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-no-space-left",
        "agent": "https://trove.xin/agent/problems/wsl-no-space-left"
      }
    },
    {
      "id": "wsl-pipe-encoding",
      "title": "PowerShell 里调 wsl 输出乱码（每字符带空字节 / 每字符一行 / 中文变 �）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/microsoft/WSL/issues/13093",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "PowerShell（5.1 和 7）里调用 `wsl.exe ...` 时输出乱码：`w s l :  ...` 每个字符中间有空格（UTF-16LE 的 00 字节被当字符显示）、`wsl --help | more` 每字符一行（[microsoft/WSL#13093](https://github.com/microsoft/WSL/issues/13093) 官方 issue）、中文内容变成 �。",
          "",
          "**直接** `wsl echo hi > 文件`（cmd 重定向）字节正常；坏的是**管道/捕获**场景：PowerShell 脚本赋值、CI 抓取、`|` 管道。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "wsl.exe 在管道/非 TTY 场景输出 **UTF-16LE（无 BOM）**，PowerShell 捕获后按自己的 stdout 编码（实测 PS 5.1 与 pwsh 7 的 -Command 链路都是 UTF-16LE 无 BOM）写到下游；下游按 UTF-8/单字节解码 → 每个 ASCII 字符被 00 隔开，中文全变 �。",
          "",
          "这是 wsl.exe 的已知行为，2025 年的 #13093（WSL 2.5.7）仍存在。",
          "",
          "wsl.exe 是 Windows 程序，它决定输出编码的依据是**输出目的地**：连到终端时按 UTF-8，连到管道/非 TTY 时按 Windows 原生宽字符 UTF-16LE（无 BOM）输出——这是 wsl.exe 的既定行为，不是 bash 决定的。",
          "",
          "PowerShell 捕获后按自己的 stdout 编码继续传递，下游程序（按 UTF-8/单字节假设）解码时把每个 ASCII 字节后面的 00 也当字符，于是「中文字符变 �、ASCII 被 00 隔开」。",
          "",
          "确定性推论：管道场景不要依赖 wsl 输出做文本解析，两条正路——`wsl.exe -e bash -lc \"...\"` 让 bash 层接管输出编码，或在 PowerShell 里 `[Console]::OutputEncoding=[Text.Encoding]::UTF8` 显式声明；",
          "",
          "写文件用 `wsl bash -lc 'cmd > file'` 让重定向发生在 Linux 侧，绕开 wsl.exe 的编码判定。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键（在 PowerShell 脚本中执行）：设置官方 UTF-8 开关，wsl 输出即恢复正常",
          "$env:WSL_UTF8 = \"1\"",
          "```",
          "",
          "放在脚本开头；或临时 `cmd /c \"set WSL_UTF8=1 && wsl ...\"`。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 设 $env:WSL_UTF8 = \"1\" — **必做**",
          "",
          "微软官方开关，实测 pwsh -File 下 wsl 输出变 UTF-8 正常；不想改全局就放在脚本开头。",
          "",
          "### 2. 复杂/长命令别硬拼命令行 — **必做**",
          "",
          "转义、长度都会炸。把脚本写到 WSL 内文件再执行：",
          "",
          "```powershell",
          "wsl bash /tmp/x.sh",
          "```",
          "",
          "PowerShell 写文件走 UNC：`Set-Content \\\\wsl.localhost\\Ubuntu\\tmp\\x.sh`。",
          "",
          "### 3. 超长/特殊字符用 base64 传输 — **必做**",
          "",
          "```powershell",
          "wsl bash -lc \"echo <base64> | base64 -d | bash\"",
          "```",
          "",
          "实测通过，绕开引号/转义/长度三坑。",
          "",
          "### 4. 输出走文件 — **必做**",
          "",
          "让 wsl 内命令把结果写 `/tmp/out.txt`，再用 `Get-Content \\\\wsl.localhost\\Ubuntu\\tmp\\out.txt` 读——UTF-8 文件直读无编码问题，比管道可靠。",
          "",
          "### 5. 辅助手段 — **必做**",
          "",
          "`[Console]::OutputEncoding = [Text.Encoding]::UTF8` 影响 PowerShell 写控制台编码，可配合 1 使用。WSL 启动横幅（\"检测到 localhost 代理配置...\"）是 stderr 噪声：设了 WSL_UTF8 后不乱码，仍介意可忽略或加 `2>$null`。"
        ],
        "怎么确认修好了": [
          "",
          "字节级验证（实测输出形态）：",
          "",
          "```text",
          "ps1 里 $env:WSL_UTF8=\"1\" 后 & wsl.exe echo hi → 字节 77 73 6C 3A 20 E6 A3 80（UTF-8 正常，中文不丢）",
          "```",
          "",
          "复现基线：`pwsh -NoProfile -Command \"wsl echo hi\"` → 字节 `77 00 73 00 6C 00 ...`（UTF-16LE 无 BOM，实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| cmd 重定向 | 字节正常（UTF-8，实测对照） |",
          "| PowerShell 管道/捕获 | UTF-16LE 无 BOM（PS 5.1 与 pwsh 7 均复现，#13093 同款） |",
          "| base64 通道 | `wsl bash -lc \"echo <b64> | base64 -d\"` 实测通过 |",
          "| 文件中转 | /tmp/out.txt + UNC 读取，无编码问题（天天在用） |",
          "| stderr 横幅 | WSL_UTF8 后不乱码，介意加 2>$null |",
          ""
        ],
        "症状": [
          "",
          "PowerShell（5.1 和 7）里调用 `wsl.exe ...` 时输出乱码：",
          "",
          "```",
          "w s l :  �hKm0R ...   # 每个字符中间有空格（UTF-16LE 的 00 字节被当字符显示）",
          "wsl --help | more     # 每字符一行（[microsoft/WSL#13093](https://github.com/microsoft/WSL/issues/13093) 官方 issue）",
          "中文内容变成 � 一类替换符",
          "```",
          "",
          "特征：**直接** `wsl echo hi > 文件`（cmd 重定向）字节正常（UTF-8）；",
          "坏的是**管道/捕获**场景：PowerShell 脚本赋值、CI 抓取、`|` 管道；",
          "WSL 启动时还会混入一段代理检测横幅（stderr），加剧观感。",
          ""
        ],
        "原因": [
          "",
          "wsl.exe 在管道/非 TTY 场景输出 **UTF-16LE（无 BOM）**，PowerShell 捕获后按自己的 stdout 编码",
          "（实测 PS 5.1 与 pwsh 7 的 -Command 链路都是 UTF-16LE 无 BOM）写到下游；",
          "下游按 UTF-8/单字节解码 → 每个 ASCII 字符被 00 隔开，中文全变 �。",
          "这是 wsl.exe 的已知行为，2025 年的 #13093（WSL 2.5.7）仍存在。",
          ""
        ],
        "解法": [
          "",
          "1. **脚本里设 `$env:WSL_UTF8 = \"1\"`** 再调 wsl（微软官方开关，实测 pwsh -File 下 wsl 输出变 UTF-8 正常）；",
          "   不想改全局就放在脚本开头，或临时 `cmd /c \"set WSL_UTF8=1 && wsl ...\"`。",
          "2. **复杂/长命令别硬拼命令行**（转义、长度都会炸）：把脚本写到 WSL 内文件再执行：",
          "   `wsl bash /tmp/x.sh`；PowerShell 写文件走 UNC：`Set-Content \\\\wsl.localhost\\Ubuntu\\tmp\\x.sh`。",
          "3. **超长/特殊字符用 base64 传输**：`wsl bash -lc \"echo <base64> | base64 -d | bash\"`（实测通过，绕开引号/转义/长度三坑）。",
          "4. **输出走文件**：让 wsl 内命令把结果写 `/tmp/out.txt`，再用 `Get-Content \\\\wsl.localhost\\Ubuntu\\tmp\\out.txt` 读——",
          "   UTF-8 文件直读无编码问题，比管道可靠。",
          "5. 辅助手段：`[Console]::OutputEncoding = [Text.Encoding]::UTF8` 影响 PowerShell 写控制台编码，可配合 1 使用。",
          "6. WSL 启动横幅（\"检测到 localhost 代理配置...\"）是 stderr 噪声：设了 WSL_UTF8 后不乱码，仍介意可忽略或加 `2>$null`。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，Windows 11 + WSL Ubuntu，PowerShell 5.1 与 pwsh 7）：",
          "  - 对照：cmd 直调 `wsl.exe echo hi > 文件` → 字节 `68 69 0A`（UTF-8，正常）；",
          "  - 复现：`pwsh -NoProfile -Command \"wsl echo hi\" > 文件` → 字节 `77 00 73 00 6C 00 ...`（UTF-16LE 无 BOM），",
          "    下游按 UTF-8 显示即 \"w s l :\" 乱码——与本条目开头截图同款，#13093 同款；PowerShell 5.1 同样复现；",
          "  - 修复：ps1 里 `$env:WSL_UTF8=\"1\"` 后 `& wsl.exe echo hi` → 字节 `77 73 6C 3A 20 E6 A3 80`（UTF-8 正常，中文不丢）；",
          "  - base64 通道：`wsl bash -lc \"echo dGVzdA== | base64 -d\"` → test（exit 0）；",
          "  - 文件中转：WSL 内写 /tmp/out.txt 后 UNC 读取，无编码问题（实践天天在用）。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-pipe-encoding",
        "agent": "https://trove.xin/agent/problems/wsl-pipe-encoding"
      }
    },
    {
      "id": "wsl-register-failed",
      "title": "WSL 启动发行版报 WslRegisterDistribution failed（0x800701bc / 0x80370102）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "https://learn.microsoft.com/windows/wsl/troubleshooting + https://learn.microsoft.com/windows/wsl/install-manual",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "刚装完 WSL 或 Windows 更新之后，双击 Ubuntu（或任何发行版）黑窗一闪，报：",
          "",
          "```",
          "WslRegisterDistribution failed with error: 0x800701bc",
          "Error: 0x800701bc WSL 2 ? ? ?",
          "```",
          "",
          "中文系统上错误描述常显示乱码或不完整，Windows 11 常见 `Error code: Wsl/Service/...`。0x800701bc 是**最高频**的 WSL 启动失败码——绝大多数是「WSL1 升级到 WSL2 后内核更新包没装」。",
          "",
          "同一族的 0x80370102 / 0x80040154 / 0x80370114 / 0x800706f7 各代表不同根因（虚拟化没开 / 功能被禁用 / 组件缺失 / 网络软件拦截），**先认错误码再动手**，别挨个试。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "「注册发行版」= Windows 的 WSL 服务（LxssManager）为发行版创建 WSL2 虚拟机并挂载文件系统。",
          "",
          "这一步依赖三样东西：**WSL2 内核**（`C:\\Windows\\System32\\lxss\\tools\\kernel`）、**虚拟机平台功能**（Virtual Machine Platform / hypervisorlaunchtype）、**WSL 服务本身**（COM 组件）。",
          "",
          "错误码就是这三样缺哪样的索引：",
          "",
          "- 0x800701bc = 内核组件缺失（WSL1→WSL2 升级后没装内核更新包；Store 版 WSL 没 `wsl --update` 到最新）；",
          "- 0x80370102 = 虚拟化没生效（BIOS 里关了 / 「虚拟机平台」功能没开 / hypervisorlaunchtype 不是 auto）；",
          "- 0x80370114 = 缺「虚拟机监控程序平台」功能（Win11 新装常见）；",
          "- 0x80040154 = WSL 功能或 COM 注册损坏（常见于 Windows Update 后功能被禁用）；",
          "- 0x800706f7 = 第三方网络软件（代理/安全软件）拦截 WSL 服务的内部 RPC 通信。",
          "",
          "WSL2 的发行版不是\"解压个文件夹\"，而是由 LxssManager 服务创建一台轻量 VM：内核由 Windows 侧的 `lxss/tools/kernel` 提供，磁盘是 vhdx 虚拟盘。注册失败 = 创建 VM 的某个前置条件没满足，错误码恰好按\"内核→虚拟化→功能→服务→网络\"分层。",
          "",
          "0x800701bc 的\"内核缺失\"是历史包袱：WSL1 时代发行版是进程隔离不需要内核，升级 WSL2 后内核更新包必须单独装（或 Store 版 `wsl --update`），没装就报这个码。",
          "",
          "确定性推论：**同一个错误码 = 同一层根因**，先看错误码再选修复命令，比按时间顺序试 5 条命令快一个数量级；修完重启终端/系统再验证，因为功能开关和 hypervisorlaunchtype 都要重启才生效。",
          ""
        ],
        "快速修复": [
          "",
          "```powershell",
          "# 管理员 PowerShell，按你的错误码选一条：",
          "# 0x800701bc（内核缺失，最高频）—— Store 版 WSL 首选：",
          "wsl --update",
          "# 0x800701bc 非 Store 版：装官方内核更新包 wsl_update_x64.msi（见官方手动安装指南）",
          "",
          "# 0x80370102（虚拟化没生效）：",
          "bcdedit /set hypervisorlaunchtype auto",
          "# 然后：控制面板 → 启用或关闭 Windows 功能 → 勾选「虚拟机平台」→ 重启",
          "",
          "# 0x80370114（缺虚拟机监控程序平台）：",
          "dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart",
          "",
          "# 0x80040154（功能被禁用，常见于 Windows Update 后）：",
          "# 控制面板 → 启用或关闭 Windows 功能 → 勾选「适用于 Linux 的 Windows 子系统」+「虚拟机平台」→ 重启",
          "",
          "# 0x800706f7（网络软件拦截）：",
          "netsh winsock reset",
          "```",
          "",
          "全部都要**管理员**运行；涉及功能开关/启动类型的必须**重启**后才生效。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 先认错误码 — **必做**（错误码决定根因层，别乱试）",
          "",
          "看报错原文里的十六进制码（0x800701bc / 0x80370102 / 0x80040154 / 0x80370114 / 0x800706f7），对照上表选路径。看不出码 → 管理员 PowerShell `wsl --status` / 重开发行版看完整报错。",
          "",
          "### 2. 0x800701bc：补内核 — **必做**（最高频，绝大多数是这个）",
          "",
          "Store 版：`wsl --update`（必要时加 `--web-download`）。非 Store 版：从微软官方手动安装指南下载 `wsl_update_x64.msi` 安装。然后重开发行版。",
          "",
          "### 3. 0x80370102 / 0x80370114：开虚拟化 — **按诊断结果**（BIOS/功能/启动类型三层）",
          "",
          "先 `bcdedit /set hypervisorlaunchtype auto`（管理员），再启用「虚拟机平台」（控制面板功能开关），BIOS 里确认虚拟化（VT-x/AMD-V）没被关。都做了仍不行 → 关闭「虚拟机平台」→ 重启 → 再打开 → 再重启（微软 Q&A 官方推荐）。",
          "",
          "### 4. 0x80040154：重开功能 — **按诊断结果**（Windows Update 后常见）",
          "",
          "控制面板勾选「适用于 Linux 的 Windows 子系统」+「虚拟机平台」，重启。还报 → 卸载 Store 版 WSL 重装，或 `wsl --update --rollback` 回滚刚更新的 WSL。",
          "",
          "### 5. 0x800706f7：清 Winsock — **按诊断结果**（代理/安全软件嫌疑）",
          "",
          "管理员 `netsh winsock reset`，重启。还不行 → 临时退出代理/安全软件再试（定位拦截源）。",
          ""
        ],
        "怎么确认修好了": [
          "",
          "```powershell",
          "wsl --version   # 正常输出 WSL 版本 + 内核版本（不再报错误码）",
          "wsl -d Ubuntu   # 发行版能进入 shell",
          "```",
          "",
          "两条都过 = 注册链路通了。注意：修完**重启后**才验证（功能开关/启动类型重启才生效），别在重启前误判失败。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| Win10 vs Win11 | 错误码家族相同；Win11 的「虚拟机监控程序平台」默认关，0x80370114 更常见 |",
          "| Store 版 vs 内置版 WSL | Store 版用 `wsl --update`；内置版（老）要装内核更新包 msi |",
          "| WSL1 升 WSL2 | 0x800701bc 高发区：升级后必须补内核更新包 |",
          "| 新装机器 | 0x80370102/0x80370114 高发：先开「虚拟机平台」再装发行版 |",
          ""
        ],
        "症状": [
          "",
          "```",
          "WslRegisterDistribution failed with error: 0x800701bc",
          "Error: 0x800701bc WSL 2 ? ? ?   （中文乱码常见）",
          "Installing, this may take a few minutes... → 立刻失败",
          "```",
          "",
          "特征：**首次启动**发行版即失败；黑窗一闪；错误码稳定可复现（同机同码）。",
          ""
        ],
        "原因": [
          "",
          "WSL2 注册发行版 = LxssManager 创建轻量 VM，前置条件四件套：WSL2 内核、虚拟机平台功能、hypervisorlaunchtype、WSL 服务/COM。错误码分层索引缺失项：0x800701bc=内核缺失（WSL1→2 升级后未装内核更新包，最高频）；0x80370102=虚拟化未生效；0x80370114=缺虚拟机监控程序平台；0x80040154=功能被禁用/COM 损坏（Windows Update 后常见）；0x800706f7=第三方网络软件拦 RPC。",
          ""
        ],
        "解法": [
          "",
          "1. 认错误码（见上表）；2. 0x800701bc → `wsl --update` 或装内核更新包 msi；3. 0x80370102/0x80370114 → `bcdedit /set hypervisorlaunchtype auto` + 启用「虚拟机平台」+ 重启；4. 0x80040154 → 重开「适用于 Linux 的 Windows 子系统」功能 + 重启，不行重装 WSL；5. 0x800706f7 → 管理员 `netsh winsock reset` + 重启。全部管理员权限，功能类修复要重启后验证。",
          ""
        ],
        "验证证据": [
          "",
          "- 本机无法复现（定制预构建版 WSL 正常运行），证据 = 官方机制核对日志：实测复现日志（2026-08-31）",
          "- 健康判据已在本机验证：`wsl --version` 正常输出 12 行版本信息；`C:\\Windows\\System32\\lxss\\tools\\kernel` 存在；`wsl bash -lc` 可进入发行版。",
          "- 错误码语义与修复命令来自微软官方 troubleshooting / install-manual / 微软 Q&A 官方回复（URL 见机制核对日志），未在本机执行修复命令（无故障可修，且部分需管理员/重启）。",
          ""
        ],
        "变体速查": [
          "",
          "| 错误码 | 一句话根因 | 一条命令 |",
          "|---|---|---|",
          "| 0x800701bc | 内核更新包缺失 | `wsl --update` |",
          "| 0x80370102 | 虚拟化没生效 | `bcdedit /set hypervisorlaunchtype auto` + 开「虚拟机平台」+ 重启 |",
          "| 0x80370114 | 缺虚拟机监控程序平台 | `dism /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart` |",
          "| 0x80040154 | 功能被禁用/COM 损坏 | 重开「适用于 Linux 的 Windows 子系统」+ 重启 |",
          "| 0x800706f7 | 网络软件拦服务 | `netsh winsock reset` + 重启 |",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - 本页是「WSL 启动失败」canonical；相邻 WSL 问题：wsl-dns-resolution-failure（启动后没网/DNS 解析失败）、wsl-no-space-left（启动后写不进盘）、wsl-pipe-encoding（PowerShell 调 wsl 乱码）。"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-register-failed",
        "agent": "https://trove.xin/agent/problems/wsl-register-failed"
      }
    },
    {
      "id": "wsl-systemd-not-running",
      "title": "WSL 里 systemctl 报 System has not been booted with systemd / 服务起不来",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "container-wsl",
      "category_label": "容器与 WSL",
      "applies_to": [
        "wsl"
      ],
      "evidence": "Evidence details kept locally (2026-08-31)",
      "source": "微软官方文档机制核对 + 本机对照（2026-08-31）",
      "recorded": "2026-08-31",
      "sections": {
        "你会遇到什么": [
          "",
          "在 WSL 里 `systemctl start docker` / `systemctl status xxx` 报 `System has not been booted with systemd as init system (PID 1). Can't operate.`（或 `Failed to connect to bus`）。",
          "",
          "照着网上「service 起服务」教程在 WSL 里全失灵——**WSL2 默认 PID1 不是 systemd**，`[boot] systemd=true` 没配就是这种状态。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "WSL2 的轻量虚拟机里，PID1 默认是微软自己的 init（不是 systemd）。`systemctl` 必须 systemd 作为 PID1 才能操作服务总线，否则直接报上面那句。2022 年 9 月起 WSL 支持在 `/etc/wsl.conf` 加 `[boot] systemd=true` 让 systemd 成为 PID1。",
          "",
          "systemd 的运作前提是「它是 PID1」：它要接管 cgroup、挂载、socket 与 service 状态。WSL 的默认 init 只做最基本的启动，不提供 service 语义，所以 `systemctl`/`journalctl` 全部失灵。",
          "",
          "启用路径 = wsl.conf 的 `[boot]` 段 + **重启发行版**（`wsl --terminate <发行版>` 或 `wsl --shutdown`）——wsl.conf 只在启动时读取，改完不重启等于没改，这是本坑第二高频原因。",
          "",
          "确定性推论：报 `System has not been booted with systemd` = wsl.conf 缺 `[boot] systemd=true`（或没重启）；`ps -p 1 -o comm=` 输出 `systemd` 才算真的生效。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# WSL 内：启用 systemd",
          "echo -e \"[boot]\\nsystemd=true\" >> /etc/wsl.conf",
          "```",
          "",
          "```powershell",
          "# Windows 侧：重启发行版（必须，否则不生效）",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          "",
          "```bash",
          "# 验证",
          "ps -p 1 -o comm=        # 必须输出 systemd",
          "systemctl is-system-running",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 确认 PID1 是谁 — **必做**",
          "",
          "```bash",
          "ps -p 1 -o comm=",
          "```",
          "",
          "输出 `systemd` = 已启用，报错另有原因；输出 `init` = 没启用。",
          "",
          "### 2. 写 wsl.conf — **必做**",
          "",
          "```bash",
          "echo -e \"[boot]\\nsystemd=true\" >> /etc/wsl.conf",
          "```",
          "",
          "已有 `[boot]` 段就只加 `systemd=true` 行。",
          "",
          "### 3. 重启发行版 — **必做**",
          "",
          "```bash",
          "# Windows 侧（PowerShell/CMD）",
          "wsl --terminate <发行版名>",
          "wsl -d <发行版名>",
          "```",
          "",
          "### 4. 验证服务总线可用 — **必做**",
          "",
          "```bash",
          "ps -p 1 -o comm=          # systemd",
          "systemctl is-system-running   # running / degraded 都算可用",
          "systemctl start docker    # 原命令现在能跑",
          "```",
          ""
        ],
        "怎么确认修好了": [
          "",
          "`ps -p 1 -o comm=` 输出 `systemd`；`systemctl is-system-running` 不再报 booted 错误；要启的服务 `systemctl status <服务>` 正常。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 未启用 systemd | PID1 = init；systemctl 报 System has not been booted with systemd |",
          "| 已启用 | PID1 = systemd；systemctl/journalctl 全可用 |",
          "| 老版本 WSL（< 0.67.6 / 2022-09 前） | 不支持 [boot] systemd=true，需先 wsl --update |",
          ""
        ],
        "症状": [
          "",
          "```",
          "$ systemctl status docker",
          "System has not been booted with systemd as init system (PID 1). Can't operate.",
          "Failed to connect to bus: Host is down",
          "```",
          "",
          "特征：`systemctl` 任何子命令都报 booted 错误；`ps -p 1` 不是 systemd；wsl.conf 没有 `[boot] systemd=true` 或改完没重启。",
          ""
        ],
        "原因": [
          "",
          "WSL2 默认 PID1 是 WSL 自带 init，不是 systemd；systemctl 需要 systemd 作 PID1。`/etc/wsl.conf [boot] systemd=true` 启用 systemd 后必须重启发行版才重新初始化。",
          ""
        ],
        "解法": [
          "",
          "1. `ps -p 1 -o comm=` 确认 PID1；",
          "2. wsl.conf 加 `[boot] systemd=true`；",
          "3. Windows 侧 `wsl --terminate <发行版名>` 重启；",
          "4. 验证 `ps -p 1 -o comm=` = systemd 后重跑原命令。",
          ""
        ],
        "验证证据": [
          "",
          "- 机制核对：微软官方文档（learn.microsoft.com/windows/wsl/systemd）；",
          "- 本机对照（2026-08-31）：定制预构建版 PID1 = systemd、`systemctl is-system-running` = degraded、wsl.conf 已有 `[boot] systemd=true`——健康路径验证；未做禁用复现（避免影响工作环境）；",
          "- 实测复现日志（2026-08-31）",
          ""
        ],
        "相关 canonical": [
          "",
          "按「前置 → 同根 / 同域 / 相关」读：",
          "",
          "- **相关（报错相近，根因不同）**：",
          "  - [WSL 注册发行版失败错误码族](wsl-register-failed.html)（WSL 发行版生命周期）",
          "  - [WSL 默认用户变 root](wsl-default-user-root-after-import.html)（wsl.conf 另一坑：改完必须重启发行版）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/wsl-systemd-not-running",
        "agent": "https://trove.xin/agent/problems/wsl-systemd-not-running"
      }
    },
    {
      "id": "yfinance-history-timeout",
      "title": "yfinance history 一直超时（Cookie/crumb fetch failed / curl 28）",
      "domain": "gateway",
      "domain_label": "网关与路由",
      "category": "network-mirror",
      "category_label": "网络与镜像",
      "applies_to": [
        "yfinance"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://www.cnblogs.com/amosli/p/22652811",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "`yf.Ticker(...).history(...)` 一跑就失败：日志先是 `Cookie fetch from fc.yahoo.com failed (Timeout), continuing without it`，然后是 `Cookie/crumb fetch failed ... continuing without crumb`，最后 `Failed to get ticker ... curl: (28) Connection timed out after 10001 milliseconds`。",
          "",
          "在国内直连、或代理只放行部分域名时最容易出现——甚至 chart 接口能通，history 却一直挂。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "yfinance 取数据前先请求两个辅助接口：`fc.yahoo.com` 拿 Cookie、`query1.finance.yahoo.com/v1/test/getcrumb` 拿 crumb。这些域在国内直连普遍超时/不可达；代理如果只放行了部分域名，就会出现\"chart 直连通、history 挂\"的表象。",
          "",
          "| 场景 | 现象（实测） |",
          "|---|---|",
          "| 国内直连 | fc.yahoo.com 超时 → crumb 失败 → `curl: (28)` |",
          "| 代理部分放行 | chart 通、history 挂（漏放 fc/query1/query2 之一） |",
          "| 1.6.x | crumb 获取失败 → 直接失败 |",
          "| 1.7.x | cookie/crumb 失败仅告警，自动轮询 query1/query2；数据通道整体不可达仍失败 |",
          "",
          "yfinance 拉历史数据是「三接口链」：先 `fc.yahoo.com` 拿 Cookie，再 `query1.finance.yahoo.com/v1/test/getcrumb` 拿 crumb，最后才用 crumb 调数据接口——前两步是前置依赖，任何一步不可达，历史数据就取不到。",
          "",
          "「chart 直连通、history 挂」的表象来自部分放行：代理只放行了数据域名，漏放 fc/query1/query2 之一。",
          "",
          "版本差异在失败语义：1.6.x crumb 失败直接失败；1.7.x 改为告警 + 自动轮询 query1/query2（数据通道整体不可达仍失败）。",
          "",
          "确定性推论：`curl: (28)` 超时先定位是哪一环——分别 curl 三个域名；国内直连 = 三个都走代理（或全直连换数据源）；代理场景 = 把 fc.yahoo.com、query1/query2.finance.yahoo.com 全部放行；1.7.x 下「有告警但能出数据」是正常降级，不是故障；判断路径：错误发生在 crumb 阶段 = 前置链断，发生在数据阶段 = 数据通道断。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：升级到 yfinance>=1.7 并用 curl_cffi 的 firefox 指纹会话走代理（示例 SOCKS5 端口按你的代理改）",
          "pip install -U yfinance curl_cffi && python3 -c \"import yfinance as yf; yf.download('AAPL', period='5d', proxy='socks5h://127.0.0.1:7890')\"",
          "```",
          "",
          "用你本地代理的实际端口（示例 7890）；这一步同时解决\"版本太老\"和\"通道不可达\"。",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 升级 yfinance — **必做**",
          "",
          "```bash",
          "pip install -U yfinance curl_cffi",
          "```",
          "",
          "≥1.7 对 cookie/crumb 失败宽容，还能自动轮询 query1/query2。",
          "",
          "### 2. 走代理（直连被墙时） — **必做**",
          "",
          "用 curl_cffi 会话 + firefox 指纹 + HTTP/2，把 http/https 都指向代理（实测 SOCKS5 与 HTTP 代理均可用）：",
          "",
          "```bash",
          "python3 - <<'EOF'",
          "from curl_cffi import requests",
          "s = requests.Session(impersonate=\"firefox\", proxies={\"http\": \"socks5h://127.0.0.1:7890\", \"https\": \"socks5h://127.0.0.1:7890\"})",
          "EOF",
          "```",
          "",
          "注意把 SOCKS5 换成你的实际端口；`socks5h` 让 DNS 也走代理。",
          "",
          "### 3. 代理规则放行全部 Yahoo 端点 — **必做**",
          "",
          "`fc.yahoo.com` + `query1/query2.finance.yahoo.com` 都要可达。漏放任意一个 = 部分请求超时（文章场景）。",
          "",
          "### 4. 定位卡点 — **排查**",
          "",
          "```bash",
          "yf.enable_debug_mode",
          "```",
          "",
          "看日志卡在哪个域；再用同一 session 直接 GET `https://query1.finance.yahoo.com/v8/finance/chart/...` 对比是否可达。"
        ],
        "怎么确认修好了": [
          "",
          "history 请求能正常返回数据（实测输出形态：14 行数据）：",
          "",
          "```text",
          "yf.Ticker(\"AAPL\").history(period=\"5d\")",
          "→ DataFrame（14 行 × 7 列，Open/High/Low/Close/Volume…）",
          "```",
          "",
          "日志里不再出现 `Cookie/crumb fetch failed` 与 `curl: (28)` 超时。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| WSL 直连 | 实测被墙，完整复现报错链（fc.yahoo.com 超时 → crumb 失败 → curl 28） |",
          "| Windows 侧 SOCKS5 代理（127.0.0.1:7890） | 实测全通：chart 200（HTTP/3）+ history OK（14 行） |",
          "| 1.7.0 + hosts 阻断 fc.yahoo.com | 实测仅 cookie 失败仍成功 |",
          "| 1.7.0 + 再阻断 query1 | 实测 crumb 失败仍成功（自动切 query2） |",
          "| 代理漏放行某域 | 部分请求超时，需放行 fc/query1/query2 全部 |",
          ""
        ],
        "症状": [
          "",
          "`yf.Ticker(...).history(...)` 反复失败，日志先出现 `Cookie fetch from fc.yahoo.com failed (Timeout), continuing without it`、`Cookie/crumb fetch failed ... continuing without crumb`，最终 `Failed to get ticker ... curl: (28) Connection timed out after 10001 milliseconds`。典型场景：国内网络直连 Yahoo、或代理只放行了部分域名（Chart API 能通但 history 挂）。",
          ""
        ],
        "原因": [
          "",
          "yfinance 取数据前先请求两个辅助接口：`fc.yahoo.com` 拿 Cookie、`query1.finance.yahoo.com/v1/test/getcrumb` 拿 crumb。国内直连这两个域超时/被墙；代理部分放行时也会出现「chart 直连通、history 挂」的表象。版本差异（实测）：1.6.x 里 crumb 获取失败会直接失败；1.7.x 已放宽（cookie/crumb 失败仅告警，数据端点可轮询 query1/query2），但**数据通道整体不可达时仍然失败**。",
          ""
        ],
        "解法": [
          "",
          "1. **直连超时 → 走代理**：SOCKS5 与 HTTP 代理均实测可用（如 `socks5h://127.0.0.1:7890`），配合 curl_cffi session（firefox 指纹 + HTTP/2）：",
          "   `requests.Session(impersonate=\"firefox\", http_version=CurlHttpVersion.V2TLS, proxies={\"http\": proxy, \"https\": proxy})`",
          "2. **代理规则要放行全部 Yahoo 端点**：`fc.yahoo.com` + `query1/query2.finance.yahoo.com` 都要可达；漏放行某个域 = 部分请求超时（文章场景）。",
          "3. **升级 yfinance ≥ 1.7**：对 cookie/crumb 失败宽容 + 端点轮询降级，可降低偶发失败。",
          "4. **定位卡点**：`yf.enable_debug_mode` 看日志卡在哪个域；用同一 session 直接 `get(\"https://query1.finance.yahoo.com/v8/finance/chart/...\")` 对比。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29）：yfinance 1.7.0 + curl_cffi 0.16.2，WSL 直连 vs Windows 侧 SOCKS5 代理（127.0.0.1:7890）。",
          "- 直连（被墙）：完整复现文章报错链（fc.yahoo.com 超时 → crumb 失败 → `curl: (28) Connection timed out`）。",
          "- 代理全通：chart 200（HTTP/3）+ history OK（14 行）。",
          "- 机制细节：hosts 阻断 fc.yahoo.com → 1.7.0 仅 cookie 失败仍成功；再阻断 query1 → crumb 失败仍成功（自动切 query2）——验证版本差异。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/yfinance-history-timeout",
        "agent": "https://trove.xin/agent/problems/yfinance-history-timeout"
      }
    },
    {
      "id": "zip-gbk-filename",
      "title": "zip 解压后中文文件名乱码（Windows/360 压缩的文件在 Linux 上乱码）",
      "domain": "coding",
      "domain_label": "编码代理与终端",
      "category": "encoding-format",
      "category_label": "编码与格式",
      "applies_to": [
        "zip"
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "source": "https://github.com/gildas-lormeau/zip.js/issues/181",
      "recorded": "2026-08-29",
      "sections": {
        "你会遇到什么": [
          "",
          "从 Windows（WinRAR/360 压缩/好压）收到的 zip，在 Linux/WSL 解压后中文文件名变乱码：`unzip -l 文件.zip` 显示 `�����ļ�.txt`（应为「测试文件.txt」）。内容（文件内容）正常，只有文件名坏；UTF-8 正常标记的 zip（Linux/macOS 压缩）不受影响。",
          "",
          "> 本文路径：**快速修复**（30 秒一段命令）→ **一步步排查**（可跳读）→ **原理与变体**（症状/原因/证据）。赶时间直接跳到「快速修复」。",
          ""
        ],
        "为什么会这样": [
          "",
          "zip 规范里文件名**默认无编码声明**：Windows 压缩软件（360 压缩/WinRAR 老版本等）用本地代码页 GBK/GB2312 存文件名且不置 UTF-8 标志位；Linux 的 unzip 按默认字符集（CP437）解码 → 乱码。zip.js#181 就是这个场景的经典提问。",
          "",
          "zip 规范的「文件名编码」设计是历史遗留：格式没有强制编码声明，Windows 压缩软件用本地代码页（中文版 = GBK/GB2312）写文件名；UTF-8 标志位（general purpose bit 11）是后来的约定，老软件不置位。Linux unzip 对无标志位的条目按默认字符集（CP437）解码——GBK 字节被按 CP437 解读，中文全变乱码。",
          "",
          "这不是文件损坏，是「写入端编码（GBK）× 读取端解码（CP437）」的错配。",
          "",
          "确定性推论：乱码文件名先确认写入端——Windows 压的旧包 = GBK，Linux/mac 新工具压的 = UTF-8（带标志位）；解 GBK 包用 `unzip -O GBK`（Info-ZIP 支持 `-O` 指定解压字符集）或 `7z x -mcp=936`；重新打包时用支持 UTF-8 标志位的工具（7-Zip 新版本/zip -r 原生）避免再踩；",
          "",
          "判断路径：乱码固定模式（每字错位） = 编码错配，不是文件名真乱。",
          ""
        ],
        "快速修复": [
          "",
          "```bash",
          "# 一键：指定编码解压（GBK 例；GB18030 覆盖超集，生僻字需要）",
          "unzip -O GBK 文件.zip",
          "```",
          ""
        ],
        "一步步来": [
          "",
          "### 1. 指定编码解压（最常用） — **必做**",
          "",
          "```bash",
          "unzip -O GBK 文件.zip",
          "```",
          "",
          "`-O` 指定 DOS/Windows 存档编码，Debian unzip 6.00 实测支持；或 `unzip -O GB18030 文件.zip`（覆盖 GBK 超集，部分生僻字需要）。",
          "",
          "### 2. 查看再解压 — **必做**",
          "",
          "```bash",
          "unzip -O GBK -l 文件.zip",
          "```",
          "",
          "先确认文件名正常，再正式解压。",
          "",
          "### 3. Python 方案（脚本/批量场景） — **必做**",
          "",
          "zipfile 不直接支持编码指定；可用 `unzip -O GBK`，或先解压后 `convmv -f GBK -t UTF-8 --notest 目录/` 批量改名。",
          "",
          "### 4. 对方侧根治 — **必做**",
          "",
          "让压缩方用 UTF-8 模式（WinRAR 勾选「文件名使用 UTF-8」；Windows 11 自带 zip 已是 UTF-8），或压缩前把中文名改成拼音/英文。",
          "",
          "### 5. 图形工具 — **必做**",
          "",
          "7-Zip（Linux 版 p7zip 对 GBK 名兼容较好）、KDE Ark、文件管理器大多能自动识别 GBK 名。",
          "",
          "### 6. 反向场景 — **必做**",
          "",
          "收到 UTF-8 标志位缺失但实际是 UTF-8 的文件（老 Linux 工具压缩）：`unzip -O UTF-8` 反向修正。"
        ],
        "怎么确认修好了": [
          "",
          "文件名显示正常（实测输出形态）：",
          "",
          "```text",
          "unzip -O GBK -l → 显示「测试文件.txt」（实测）",
          "实际解压到磁盘同样正常（对比 ls 输出，实测）",
          "```",
          "",
          "复现基线：`unzip -l` → `�����ļ�.txt`（乱码，实测）。",
          ""
        ],
        "其它环境的表现": [
          "",
          "| 环境 | 说明 |",
          "|---|---|",
          "| 360 压缩/WinRAR 老版 | GBK 文件名无 UTF-8 标志（zip.js#181 场景） |",
          "| -O GBK / -O GB18030 | 均可（实测）；GB18030 覆盖生僻字 |",
          "| Windows 11 自带 zip | 已是 UTF-8，不受影响 |",
          "| 图形工具 | 7-Zip / Ark / 文件管理器自动识别 |",
          "| 脚本批量 | unzip -O GBK 或 convmv 改名 |",
          ""
        ],
        "症状": [
          "",
          "从 Windows（WinRAR/360 压缩/好压）收到的 zip，在 Linux/WSL 解压后中文文件名变乱码：",
          "",
          "```",
          "$ unzip -l 文件.zip",
          "  Length  Date  Time  Name",
          "       11  2026-08-29  11:48  �����ļ�.txt     # 应为「测试文件.txt」",
          "```",
          "",
          "特征：压缩时是中文名、解压出来是 `����` 一类的替换符；",
          "内容（文件内容）正常，只有文件名坏；UTF-8 正常标记的 zip（Linux/macOS 压缩）不受影响。",
          ""
        ],
        "原因": [
          "",
          "zip 规范里文件名默认无编码声明：Windows 压缩软件（360 压缩/WinRAR 老版本等）用本地代码页",
          "GBK/GB2312 存文件名且不置 UTF-8 标志位；Linux 的 unzip 按默认字符集（CP437）解码 → 乱码。",
          "zip.js#181 就是这个场景的经典提问（解压 360 压缩的文件中文乱码）。",
          ""
        ],
        "解法": [
          "",
          "1. **指定编码解压（最常用）**：`unzip -O GBK 文件.zip`（-O 指定 DOS/Windows 存档编码，Debian unzip 6.00 实测支持）；",
          "   或 `unzip -O GB18030 文件.zip`（GB18030 覆盖 GBK 超集，部分生僻字需要）。",
          "2. **查看再解压**：先 `unzip -O GBK -l 文件.zip` 确认文件名正常，再正式解压。",
          "3. **Python 方案**（脚本/批量场景）：读取时把文件名从 GBK 解码——zipfile 不直接支持，",
          "   可用 `unzip -O GBK` 或先解压后 `convmv -f GBK -t UTF-8 --notest 目录/` 批量改名。",
          "4. **对方侧根治**：让压缩方用 UTF-8 模式（WinRAR 勾选「文件名使用 UTF-8」；Windows 11 自带 zip 已是 UTF-8），",
          "   或压缩前把中文名改成拼音/英文。",
          "5. **图形工具**：7-Zip（Linux 版 p7zip 对 GBK 名兼容较好）、KDE Ark、文件管理器大多能自动识别 GBK 名。",
          "6. 收到 UTF-8 标志位缺失但实际是 UTF-8 的文件（老 Linux 工具压缩）：`unzip -O UTF-8` 反向修正。",
          ""
        ],
        "验证证据": [
          "",
          "- 本地实测（2026-08-29，WSL Ubuntu，unzip 6.00，无网络依赖）：",
          "  - 夹具：用 python zipfile 构造 GBK 文件名 zip（12 字节占位替换为 GBK 编码的「测试文件.txt」，清掉 UTF-8 标志位，local+central 双头校验），模拟 Windows 压缩软件产物；",
          "  - 复现：`unzip -l` → 文件名显示 `�����ļ�.txt`（乱码）；",
          "  - 修复：`unzip -O GBK -l` → 显示 `测试文件.txt`；实际解压到磁盘同样正常（对比 ls 输出）；",
          "  - 说明：`-O GBK` 与 `-O GB18030` 均可；UTF-8 标志位正常的 zip 不受影响。",
          "- 实测复现日志（2026-08-29）"
        ]
      },
      "pages": {
        "human": "https://trove.xin/problems/zip-gbk-filename",
        "agent": "https://trove.xin/agent/problems/zip-gbk-filename"
      }
    }
  ],
  "domains": [
    {
      "id": "multi-agent",
      "label": "多智能体编排",
      "tools": [
        "agent-framework",
        "deepcode",
        "moltis",
        "oh-my-claudecode",
        "ruflo"
      ],
      "problems": [
        "agent-framework-2",
        "agent-framework-3",
        "agent-framework-4",
        "agent-toolchain-coexist",
        "claude-json-multi-tool",
        "deepcode-2",
        "deepcode-3",
        "deepcode-4",
        "deepcode-5",
        "deepcode-6",
        "oh-my-claudecode-1",
        "oh-my-claudecode-2",
        "oh-my-claudecode-3",
        "oh-my-claudecode-4",
        "oh-my-claudecode-5",
        "oh-my-claudecode-6",
        "oh-my-claudecode-7",
        "ruflo-1",
        "ruflo-2",
        "ruflo-3",
        "ruflo-4"
      ],
      "page": "https://trove.xin/domains/multi-agent"
    },
    {
      "id": "gateway",
      "label": "网关与路由",
      "tools": [
        "claude-code-router",
        "hoop",
        "litellm"
      ],
      "problems": [
        "claude-code-router-1",
        "claude-code-router-2",
        "claude-code-router-3",
        "claude-code-router-4",
        "claude-code-router-5",
        "claude-code-router-6",
        "yfinance-history-timeout"
      ],
      "page": "https://trove.xin/domains/gateway"
    },
    {
      "id": "session",
      "label": "会话与记忆",
      "tools": [
        "cc-switch",
        "claude-mem"
      ],
      "problems": [
        "cc-switch-1",
        "cc-switch-2",
        "cc-switch-3",
        "cc-switch-4",
        "cc-switch-5",
        "claude-mem-1",
        "claude-mem-2",
        "claude-mem-3",
        "claude-mem-4",
        "claude-mem-5",
        "claude-mem-6",
        "claude-mem-7"
      ],
      "page": "https://trove.xin/domains/session"
    },
    {
      "id": "skills",
      "label": "技能管理",
      "tools": [
        "agent-skills",
        "skilldock",
        "superpowers"
      ],
      "problems": [
        "agent-skills-1",
        "agent-skills-2",
        "agent-skills-3",
        "agent-skills-4",
        "superpowers-1",
        "superpowers-2",
        "superpowers-3",
        "superpowers-4",
        "superpowers-5",
        "superpowers-6",
        "superpowers-7"
      ],
      "page": "https://trove.xin/domains/skills"
    },
    {
      "id": "security",
      "label": "安全与治理",
      "tools": [
        "agent-governance-toolkit",
        "agentshield",
        "skillspector"
      ],
      "problems": [
        "codex-sandbox-setup-error",
        "skillspector-1",
        "skillspector-2",
        "skillspector-3",
        "skillspector-4",
        "skillspector-5",
        "skillspector-6",
        "sms-api-rate-limit",
        "tls-jdk-upgrade"
      ],
      "page": "https://trove.xin/domains/security"
    },
    {
      "id": "coding",
      "label": "编码代理与终端",
      "tools": [
        "herdr",
        "oh-my-pi",
        "playwright-mcp",
        "repomix",
        "swe-rex"
      ],
      "problems": [
        "apt-404-broken",
        "codex-cli-chatgpt-not-found",
        "codex-update-msix",
        "crlf-command-not-found",
        "crontab-env-path",
        "curl-resume-break",
        "docker-build-cache",
        "docker-compose-command-not-found",
        "docker-compose-depends-not-ready",
        "docker-container-dns",
        "docker-container-name-conflict",
        "docker-container-oom-killed",
        "docker-container-timezone-locale",
        "docker-daemon-connect",
        "docker-desktop-orphan-socket",
        "docker-exec-tty",
        "docker-host-access",
        "docker-hub-pull-fail",
        "docker-invalid-reference-format",
        "docker-manifest-not-found",
        "docker-port-already-allocated",
        "docker-prune-scope",
        "docker-volume-permission",
        "execpolicy-restricted",
        "float-crash",
        "git-crlf",
        "git-dubious-ownership",
        "github-clone-fail",
        "github-direct-connect-fail",
        "go-proxy-cn",
        "herdr-1",
        "herdr-2",
        "herdr-3",
        "hf-model-download",
        "java-oom-classify",
        "netty-direct-memory-oom",
        "node-heap-oom",
        "npm-digital-envelope-unsupported",
        "npm-eresolve-peer-conflict",
        "npm-global-eacces",
        "npm-mirror-registry",
        "oh-my-pi-1",
        "oh-my-pi-2",
        "oh-my-pi-3",
        "oh-my-pi-4",
        "pep668-externally-managed",
        "pip-mirror-source",
        "pip-ssl-cert",
        "proxy-env-var",
        "python-command-not-found",
        "python-distutils-removed",
        "python-unicode-decode",
        "python-venv-move",
        "script-permission-denied",
        "site-open-fail-others-ok",
        "ssh-key-permissions",
        "tar-strip-components",
        "uninstall-leftover",
        "wsl-default-user-root-after-import",
        "wsl-dns-resolution-failure",
        "wsl-interop-disabled",
        "wsl-no-space-left",
        "wsl-pipe-encoding",
        "wsl-register-failed",
        "wsl-systemd-not-running",
        "zip-gbk-filename"
      ],
      "page": "https://trove.xin/domains/coding"
    },
    {
      "id": "review",
      "label": "代码审查",
      "tools": [
        "code-review-graph"
      ],
      "problems": [
        "code-review-graph-2",
        "code-review-graph-3",
        "code-review-graph-4"
      ],
      "page": "https://trove.xin/domains/review"
    },
    {
      "id": "observability",
      "label": "可观测性",
      "tools": [
        "logfire"
      ],
      "problems": [
        "logfire-1",
        "logfire-2"
      ],
      "page": "https://trove.xin/domains/observability"
    },
    {
      "id": "local-llm",
      "label": "大模型本地部署",
      "tools": [
        "ollama"
      ],
      "problems": [
        "function-calling-guard",
        "llm-training-loss-spike",
        "ollama-connect-failed",
        "ollama-install-slow",
        "ollama-models-migration",
        "ollama-pull-timeout",
        "open-webui-deploy-fail",
        "open-webui-forgot-password",
        "open-webui-ollama-connect",
        "open-webui-rag-embedding"
      ],
      "page": "https://trove.xin/domains/local-llm"
    },
    {
      "id": "deploy",
      "label": "构建与部署",
      "tools": [
        "vite-deploy"
      ],
      "problems": [
        "css-load-failure",
        "minio-to-rustfs-migration",
        "mysql-binlog-flashback",
        "vite-chunkloaderror",
        "vite-history-fallback"
      ],
      "page": "https://trove.xin/domains/deploy"
    },
    {
      "id": "experience",
      "label": "经验与方法论",
      "tools": [],
      "problems": [
        "ai-coding-fatigue-consensus",
        "ai-coding-honeymoon-consensus",
        "code-review-consensus",
        "debugging-methodology-consensus",
        "doc-culture-consensus",
        "incident-review-consensus",
        "install-channel-freshness-consensus",
        "legacy-code-handoff-consensus",
        "local-llm-deploy-consensus",
        "prompt-engineering-consensus",
        "tech-debt-consensus",
        "unit-testing-consensus",
        "vibe-coding-consensus"
      ],
      "page": "https://trove.xin/domains/experience"
    }
  ],
  "conflict_matrix": [],
  "combos": [
    {
      "id": "combo-ai-code-review",
      "title": "AI 代码审查工作流（repomix + code-review-graph + swe-rex）",
      "scenario": "想让 AI 审查/理解自己的代码库，但仓库大、上下文窗口有限，喂不进去也喂不全",
      "status": "partial",
      "title_en": "Combo Draft: AI Code Review Workflow",
      "scenario_en": "Want AI to review/understand a codebase, but repos are large and context windows are limited — you can't feed it all in.",
      "recorded": "2026-08-29",
      "solves": [
        "code-review-graph-2",
        "code-review-graph-3"
      ],
      "members": [
        {
          "name": "repomix",
          "role": "代码上下文打包",
          "download": "npm 包 `repomix`；GitHub Release 单二进制（免 Node）；brew（macOS）",
          "version": ">= 1.18.0（2026-08-29 实测；Node >= 18）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g repomix\nrepomix <仓库目录> --output repomix-output.txt --compress"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "code-review-graph",
          "role": "审查",
          "download": "PyPI `code-review-graph`（官方推荐 pipx；国内用清华镜像）",
          "version": "== 2.3.8（2026-08-28 收录实测版本）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version   # 预期输出: code-review-graph 2.3.8"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "swe-rex",
          "role": "沙箱执行验证",
          "download": "PyPI `swe-rex`（官方）",
          "version": "== 1.4.0（2026-08-28 收录实测版本）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0\nswerex-remote --help   # 预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-ai-code-review",
        "agent": "https://trove.xin/agent/combos/combo-ai-code-review"
      },
      "install_script": "https://trove.xin/combos/combo-ai-code-review.sh"
    },
    {
      "id": "combo-auto-test",
      "title": "自动化测试闭环（静态分析 → 沙箱执行 → 浏览器 E2E）",
      "scenario": "改了代码想自动验证不回归——静态分析先拦、沙箱里跑得起来、浏览器里断言真实页面；三个环节各自有工具但没有成套编排",
      "status": "partial",
      "title_en": "Combo: Automated Test Loop (Static Analysis → Sandbox Execution → Browser E2E)",
      "scenario_en": "Changed code and want automated no-regression checks — static analysis first, sandbox execution, browser assertions on real pages; each stage has tools but no integrated orchestration.",
      "recorded": "2026-08-29",
      "solves": [],
      "members": [
        {
          "name": "deepcode",
          "role": "静态分析",
          "download": "PyPI 包 `deepcode-hku`；国内必须带清华镜像（官方直连当次预检 16s 边缘，可能触发 15s 读超时）",
          "version": "2.1.0；Python >= 3.12",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple deepcode-hku\ndeepcode --version   # 预期输出: DeepCode 2.1.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "swe-rex",
          "role": "沙箱执行",
          "download": "PyPI 包 `swe-rex`；国内必须带清华镜像",
          "version": "1.4.0；Python >= 3.10；沙箱执行需 Docker（本环境未实测沙箱运行）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0\nswerex-remote --help   # 预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "playwright-mcp",
          "role": "浏览器 E2E",
          "download": "npm 包 `@playwright/mcp`；国内必须带 npmmirror 前缀（2026-08-29 实测 3 秒装完）",
          "version": "0.0.79；Node >= 18；首次运行需下载浏览器引擎（chromium 113.9MiB）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g @playwright/mcp\nnpx --no-install @playwright/mcp --version\nnpx --no-install playwright install chromium"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "未实测：三工具组合链路未在同环境成套实测；分工具实测证据见各条目（playwright-mcp cli-smoke-2026-08-29 / deepcode cli-smoke / swe-rex cli-smoke）",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-auto-test",
        "agent": "https://trove.xin/agent/combos/combo-auto-test"
      },
      "install_script": "https://trove.xin/combos/combo-auto-test.sh"
    },
    {
      "id": "combo-cicd-quality",
      "title": "CI/CD 质量门禁（swe-rex + deepcode + code-review-graph）",
      "scenario": "用户想在 CI/CD 里加一道「质量门禁」——代码安全跑、提交前查问题、审查时看影响面，三个工具一次装齐不打架。",
      "status": "verified",
      "title_en": "Combo Draft: CI/CD Quality Gate (swe-rex + deepcode + code-review-graph)",
      "scenario_en": "User wants a quality gate in CI/CD — code runs safely, issues caught before commit, impact visible during review; three tools installed together without conflicts.",
      "recorded": "2026-08-29",
      "solves": [
        "code-review-graph-2",
        "npm-eresolve-peer-conflict",
        "docker-build-cache"
      ],
      "members": [
        {
          "name": "swe-rex",
          "role": "沙箱执行服务器",
          "download": "官方 PyPI（包名 swe-rex）；国内必须带清华镜像（PyPI 官方直连超时）",
          "version": "[1.4.0, 1.4.0]（manifest 锁定；Python >= 3.10；release v1.4.0 无独立资产，分发走 PyPI）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swe-rex==1.4.0\nswerex-remote --help   # 预期输出: usage: swerex-remote [-h] [--host HOST] [--port PORT] --auth-token AUTH_TOKEN"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "deepcode",
          "role": "静态分析",
          "download": "官方 PyPI（包名 deepcode-hku）；国内建议清华镜像（PyPI 官方 16s 边缘易超时，实测 40.3s 全量依赖）",
          "version": "[2.1.0, 2.1.0]（manifest 锁定；Python >= 3.12）",
          "install_cmds": [
            "pip install deepcode-hku --index-url https://pypi.tuna.tsinghua.edu.cn/simple\ndeepcode --version   # 预期输出: DeepCode 2.1.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "code-review-graph",
          "role": "审查图谱",
          "download": "官方 PyPI（包名 code-review-graph，==2.3.8）；国内必须带清华镜像（PyPI 官方直连超时）",
          "version": "[2.3.8, 2.3.8]（manifest 锁定；Python >= 3.10）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version   # 预期输出: code-review-graph 2.3.8"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-28)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-cicd-quality",
        "agent": "https://trove.xin/agent/combos/combo-cicd-quality"
      },
      "install_script": "https://trove.xin/combos/combo-cicd-quality.sh"
    },
    {
      "id": "combo-docker-compose-dev",
      "title": "docker-compose 开发环境",
      "scenario": "想用 docker-compose 一键起全套开发环境（MySQL/Redis/Nginx 等），不知道 compose 怎么写、镜像怎么拉、数据怎么留",
      "status": "verified",
      "title_en": "Combo: Docker Compose Dev Environment",
      "scenario_en": "You want a full local dev environment (MySQL/Redis/Nginx...) up with one docker-compose command, but don't know how to write the compose file, how to pull images in China, or how to keep data between restarts.",
      "recorded": "2026-08-30",
      "solves": [
        "docker-host-access",
        "docker-port-already-allocated",
        "docker-volume-permission"
      ],
      "members": [
        {
          "name": "docker compose",
          "role": "编排载体",
          "download": "Docker Desktop 自带（Windows）；Linux 装 docker-compose-plugin（Docker 官方源或发行版源）",
          "version": "v2+（实测 v5.4.0）；依赖 Docker daemon 可用",
          "install_cmds": [
            "docker compose version"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "MySQL 8.0 镜像",
          "role": "",
          "download": "`docker.m.daocloud.io/library/mysql:8.0`（官方镜像的国内镜像前缀；实测直连 docker hub 拉不动，daocloud 可拉）",
          "version": "8.0（实测 2026-08-30 拉取成功，约 500MB）",
          "install_cmds": [],
          "notes": [],
          "guide": false
        },
        {
          "name": "Redis 7 镜像",
          "role": "",
          "download": "`docker.m.daocloud.io/library/redis:7-alpine`（轻量 alpine 版，实测秒级启动）",
          "version": "7-alpine（实测 2026-08-30 拉取成功，约 100MB）",
          "install_cmds": [],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-30",
        "ttl_due": "2027-02-26",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-docker-compose-dev",
        "agent": "https://trove.xin/agent/combos/combo-docker-compose-dev"
      },
      "install_script": "https://trove.xin/combos/combo-docker-compose-dev.sh"
    },
    {
      "id": "combo-docker-wsl-env",
      "title": "容器与 WSL 环境成套配置与排查",
      "scenario": "Docker 和 WSL 装了但\"用不起来\"——daemon 连不上、端口冲突、exec 报 TTY、容器访问不了宿主机、WSL 输出乱码、报磁盘满、WSL 里找不到 node；每篇教程各讲一个坑，本组合包按「先 WSL 后 Docker」顺序成套解决",
      "status": "partial",
      "title_en": "Combo: Docker & WSL Environment Setup and Troubleshooting",
      "scenario_en": "Docker and WSL installed but “won't work” — daemon unreachable, port conflicts, exec TTY errors, containers can't reach the host, garbled WSL output, disk full, node not found in WSL; each tutorial covers one pit.",
      "recorded": "2026-08-29",
      "solves": [
        "docker-desktop-orphan-socket",
        "docker-daemon-connect",
        "wsl-no-space-left"
      ],
      "members": [
        {
          "name": "WSL",
          "role": "基础环境配置目标",
          "download": "Windows 官方（Microsoft Store / wsl --install）；非插件，是系统组件",
          "version": "WSL >= 2.4.4（wsl --manage <发行版> --set-sparse true 需要 2.4.4+，WSL 2 为运行前提）",
          "install_cmds": [
            "# 编码（问题：wsl-pipe-encoding）——现代 WSL 默认 UTF-8 管道，中文直传正常；\n  # 旧版 / Windows PowerShell 5.1 出现逐字符空字节/乱码时，改为 Windows Terminal + WSL 内 UTF-8 区域设置：\n  sudo locale-gen zh_CN.UTF-8 && sudo update-locale LANG=zh_CN.UTF-8\n  # 磁盘（问题：wsl-no-space-left）——vhdx 稀疏化，删除文件自动还空间（WSL 2.4.4+）：\n  #   Windows 侧（PowerShell）：wsl --shutdown\n  #   wsl --manage <发行版> --set-sparse true\n  # 运行时（问题：claude-code-router-2）——WSL 侧装 Node，别依赖 Windows 侧 npm：\n  curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Docker Engine",
          "role": "容器运行时配置目标",
          "download": "Docker Desktop（Windows/WSL 集成）或 Docker Engine（Linux 官方脚本 https://get.docker.com）；非插件",
          "version": ">= 20.10（registry-mirrors / host-gateway 长期支持；本机实测 29.1.3）",
          "install_cmds": [
            "# daemon（问题：docker-daemon-connect）——\"Cannot connect to the Docker daemon\" 三板斧：\n  #   1) Docker Desktop 已启动且开启 WSL 集成（Settings → Resources → WSL integration）\n  #   2) 检查 socket：ls -l /mnt/wsl/docker-desktop/shared-sockets/guest-services/docker.proxy.sock\n  #   3) 或本机直接跑 Engine：sudo systemctl enable --now docker",
            "# 排查：占用者是不是 docker-proxy，daemon 是不是 /usr/bin/dockerd（旧 daemon 特征）\n  ss -ltnp | grep <端口>\n  systemctl status docker          # 确认旧 daemon 在自启\n  # 处理：停掉并禁用 WSL 内旧 daemon（先确认其名下无在用容器再停）\n  systemctl stop docker docker.socket\n  systemctl disable docker docker.socket"
          ],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-docker-wsl-env",
        "agent": "https://trove.xin/agent/combos/combo-docker-wsl-env"
      },
      "install_script": "https://trove.xin/combos/combo-docker-wsl-env.sh"
    },
    {
      "id": "combo-env-init",
      "title": "Agent 开发环境一键初始化",
      "scenario": "新机器/新仓库想让 AI 立刻能干活——读代码、记上下文、跑会话、带技能；每个工具单独装单独配，顺序与组合没人讲清",
      "status": "partial",
      "title_en": "Combo: Agent Dev Environment One-Shot Init",
      "scenario_en": "Fresh machine/repo, want AI working immediately — read code, remember context, run sessions, carry skills; each tool installed and configured separately, no clear order or combo.",
      "recorded": "2026-08-29",
      "solves": [
        "python-command-not-found",
        "pep668-externally-managed",
        "apt-404-broken"
      ],
      "members": [
        {
          "name": "agent-framework",
          "role": "环境骨架",
          "download": "pip 包 `agent-framework`（官方 PyPI）；国内必须带清华镜像（PyPI 官方直连超时）",
          "version": "1.15.0；Python 3.10~3.13（3.14 全量安装部分依赖无预编译轮子，建议 core 精简路径）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework\n# 精简路径（只跑核心编排）：\npip install -i https://pypi.tuna.tsinghua.edu.cn/simple agent-framework-core==1.15.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "agent-skills",
          "role": "技能包",
          "download": "GitHub `addyosmani/agent-skills`（codeload tarball 0.6.7；本环境 codeload 直连可用）",
          "version": "0.6.7；Node >= 18（skills CLI 路径）",
          "install_cmds": [
            "# 方式 A：skills CLI（官方推荐，装到 agent 技能目录）\nnpx -y skills add addyosmani/agent-skills --yes --global\n# 方式 B：手工复制（国内稳，装到 ~/.claude/skills/agent-skills）\ncurl -sL https://codeload.github.com/addyosmani/agent-skills/tar.gz/refs/tags/0.6.7 | tar xz\ncp -r agent-skills-0.6.7/skills ~/.claude/skills/agent-skills"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "superpowers",
          "role": "方法论技能框架",
          "download": "GitHub `obra/superpowers`（codeload tarball v6.3.0；直连 1.7s 实测）",
          "version": "6.3.0；无运行时依赖（纯技能目录）",
          "install_cmds": [
            "curl -sL -o /tmp/superpowers.tar.gz https://codeload.github.com/obra/superpowers/tar.gz/refs/tags/v6.3.0\nmkdir -p ~/.codex/skills && tar xzf /tmp/superpowers.tar.gz -C /tmp --strip-components=1 superpowers-6.3.0/skills\ncp -r /tmp/skills ~/.codex/skills/superpowers"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "herdr",
          "role": "终端运行时",
          "download": "GitHub release 二进制 `herdr-linux-x86_64`（国内走 gh-proxy 加速，22.7MB）",
          "version": "0.8.2；Linux x86_64（无运行时依赖）",
          "install_cmds": [
            "curl -sL -o /usr/local/bin/herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64\nchmod +x /usr/local/bin/herdr && herdr --version"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "ruflo",
          "role": "元编排",
          "download": "npm 包 `ruflo`；国内用 npmmirror",
          "version": "3.38.20；Node >= 20",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect\nnpx -y ruflo --version"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "claude-mem",
          "role": "跨会话记忆",
          "download": "npm 包 `claude-mem`；国内用 npmmirror",
          "version": "13.16.1；Node >= 18；worker 运行时需 Bun",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem\nclaude-mem install --ide claude-code"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-env-init",
        "agent": "https://trove.xin/agent/combos/combo-env-init"
      },
      "install_script": "https://trove.xin/combos/combo-env-init.sh"
    },
    {
      "id": "combo-governance-stack",
      "title": "开发链路治理栈（agent-governance-toolkit + code-review-graph + herdr）",
      "scenario": "用户想让 Agent 在开发链路里「先守规矩、再写代码、再管会话」——策略门禁、代码审查图谱、会话/工作区监督三件套一次装齐。",
      "status": "verified",
      "title_en": "Combo Draft: Dev Workflow Governance Stack (agent-governance-toolkit + code-review-graph + herdr)",
      "scenario_en": "User wants agents to “follow policy first, then write code, then manage sessions” — policy gates, code-review graphs, and session/workspace supervision in one bundle.",
      "recorded": "2026-08-29",
      "solves": [
        "agent-toolchain-coexist",
        "function-calling-guard",
        "execpolicy-restricted"
      ],
      "members": [
        {
          "name": "agent-governance-toolkit",
          "role": "策略门禁与合规检查",
          "download": "官方 PyPI（包名 agent-governance-toolkit）；国内用清华镜像 `https://pypi.tuna.tsinghua.edu.cn/simple`",
          "version": "[4.1.0, 4.1.0]（manifest 锁定单版本；CLI = agt，meta 包形态；本组合只用 CLI 面）",
          "install_cmds": [
            "pip install agent-governance-toolkit --index-url https://pypi.tuna.tsinghua.edu.cn/simple\nagt --version   # 预期输出: agt, version 4.1.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "code-review-graph",
          "role": "代码关联图谱",
          "download": "官方 PyPI（包名 code-review-graph，==2.3.8）；国内必须带清华镜像（PyPI 官方直连超时，探针 2026-08-27）",
          "version": "[2.3.8, 2.3.8]（manifest 锁定；Python >= 3.10）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version   # 预期输出: code-review-graph 2.3.8"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "herdr",
          "role": "会话与工作区监督",
          "download": "官方 GitHub release（v0.8.2，herdr-linux-x86_64）；国内 GitHub 直连慢，走 gh-proxy 前缀",
          "version": "[0.8.2, 0.8.2]（manifest 锁定；Linux x86_64 单二进制，无运行时依赖）",
          "install_cmds": [
            "curl -sL -o herdr https://gh-proxy.com/https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64\nchmod +x herdr && ./herdr --version   # 预期输出: herdr 0.8.2"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-governance-stack",
        "agent": "https://trove.xin/agent/combos/combo-governance-stack"
      },
      "install_script": "https://trove.xin/combos/combo-governance-stack.sh"
    },
    {
      "id": "combo-local-llm-finetune",
      "title": "本地大模型微调/推理环境（ollama + open-webui + LLaMA-Factory）",
      "scenario": "用户想做本地大模型（微调/推理），网上教程零散、环境装不起来——一包装齐：模型下载与运行底座 + 网页界面 + 微调工具。",
      "status": "verified",
      "title_en": "Combo Draft: Local LLM Finetune / Inference Environment (ollama + open-webui + LLaMA-Factory)",
      "scenario_en": "User wants local LLMs (fine-tuning/inference); online tutorials are scattered and setups keep failing — one bundle: model download & runtime + web UI + fine-tuning tool.",
      "recorded": "2026-08-29",
      "solves": [
        "ollama-install-slow",
        "ollama-pull-timeout",
        "llm-training-loss-spike",
        "open-webui-ollama-connect"
      ],
      "members": [
        {
          "name": "ollama",
          "role": "模型下载与运行底座",
          "download": "官方 https://ollama.com/download（install.sh / Windows 安装器 / macOS dmg）；Linux 二进制 = GitHub Release 资产 `ollama-linux-amd64.tar.zst`（国内走 gh-proxy 前缀）",
          "version": ">= 0.33.2（2026-08-29 最新 v0.33.2；旧版本 < 0.30 的 API/模型兼容性差，建议直接最新）",
          "install_cmds": [
            "# Linux（官方脚本；国内 GitHub 直连慢时改用下方手动路径）\ncurl -fsSL https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/install.sh | sh\n# 或手动：下载 tar.zst 解压（需 tar --zstd 或 zstd 工具）\ncurl -sL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst\nmkdir -p /tmp/ollama-bin && tar --zstd -xf /tmp/ollama.tar.zst -C /tmp/ollama-bin\n/tmp/ollama-bin/bin/ollama --version   # 预期输出: ollama version 0.33.2"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "open-webui",
          "role": "网页界面",
          "download": "官方 Docker 镜像 `ghcr.io/open-webui/open-webui`（GitHub latest v0.11.3，2026-08-31 发布，无 release 资产 = Docker/pip 形态）；国内 ghcr 拉不动走 ghcr 镜像（ghcr.nju.edu.cn，2026-08-30 拉取实测 + 2026-09-01 插件域复测）",
          "version": ">= 0.11.1（Docker tag 建议 `:main` 跟随最新；pip 形态硬性要求 Python 3.11~3.12，实测 3.14 装不上）",
          "install_cmds": [
            "docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway \\\n    -v open-webui:/app/backend/data --name open-webui --restart always \\\n    -e ENABLE_SIGNUP=true \\\n    -e RAG_EMBEDDING_ENGINE=ollama \\\n    -e RAG_EMBEDDING_MODEL=nomic-embed-text \\\n    ghcr.nju.edu.cn/open-webui/open-webui:main\n# 国内镜像：直接写 ghcr.nju.edu.cn（2026-08-30 实测拉取成功，镜像 7.16GB）；官方 ghcr.io 慢时同前缀替换"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "LLaMA-Factory",
          "role": "本地微调工具",
          "download": "官方 PyPI（PyPI 包名 llamafactory；源码 GitHub hiyouga/LlamaFactory，最新 tag v0.9.5，2026-08-29）；国内用清华镜像",
          "version": ">= 0.9.5（Python >= 3.10；需 PyTorch 2.x，CUDA 版需先装对应 torch 轮子）",
          "install_cmds": [
            "pip install llamafactory --index-url https://pypi.tuna.tsinghua.edu.cn/simple\nllamafactory-cli version   # 预期输出: LLaMA Factory 版本号（2026-08-30 实测 0.9.5）"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "nomic-embed-text",
          "role": "知识库 embedding",
          "download": "`ollama pull nomic-embed-text`（ollama 官方 registry，274MB，2026-08-30 实测约 45s/6.3MB/s）；registry 拉不动时从 ModelScope 找 GGUF 导入（路径同 ollama 模型）",
          "version": "latest（2026-08-30 实测 v0.32.x 配套正常）",
          "install_cmds": [
            "ollama pull nomic-embed-text   # 274MB，官方 registry（实测约 45s）\ncurl -s localhost:11434/api/embed -d '{\"model\":\"nomic-embed-text\",\"input\":[\"测试\"]}'   # 预期输出: 768 维向量"
          ],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-30",
        "ttl_due": "2027-02-26",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-local-llm-finetune",
        "agent": "https://trove.xin/agent/combos/combo-local-llm-finetune"
      },
      "install_script": "https://trove.xin/combos/combo-local-llm-finetune.sh"
    },
    {
      "id": "combo-local-rag",
      "title": "本地知识库问答（ollama + MaxKB）",
      "scenario": "用户想把笔记/文档变成「能问答的私人知识库」，不知道用什么工具——一包装齐：模型底座 + 知识库问答界面，国内网络适配。",
      "status": "verified",
      "title_en": "Combo: Local Knowledge Base Q&A (ollama + MaxKB)",
      "scenario_en": "You want to turn your notes/PDFs/company docs into a private knowledge base you can ask questions against, but do not know which tools to use — one bundle: model runtime + knowledge base Q&A console, China-network adapted.",
      "recorded": "2026-09-01",
      "solves": [
        "ollama-pull-timeout",
        "ollama-install-slow",
        "ollama-connect-failed"
      ],
      "members": [
        {
          "name": "ollama",
          "role": "模型下载与运行底座",
          "download": "官方 https://ollama.com/download（install.sh / Windows 安装器 / macOS dmg）；Linux 二进制 = GitHub Release 资产 `ollama-linux-amd64.tar.zst`（国内走 gh-proxy 前缀，2026-08-29 实测）",
          "version": ">= 0.33.2（2026-08-29 实测版本；MaxKB 对接走 OpenAI 兼容 API，对版本不敏感）",
          "install_cmds": [
            "curl -fsSL https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/install.sh | sh\n# 或手动：下载 tar.zst 解压（需 tar --zstd 或 zstd 工具）\ncurl -sL -o /tmp/ollama.tar.zst https://gh-proxy.com/https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64.tar.zst\nmkdir -p /tmp/ollama-bin && tar --zstd -xf /tmp/ollama.tar.zst -C /tmp/ollama-bin\n/tmp/ollama-bin/bin/ollama --version"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "MaxKB",
          "role": "知识库问答台",
          "download": "官方 Docker 镜像 `1panel/maxkb`（Docker Hub）；国内加速前缀 `docker.m.daocloud.io/1panel/maxkb`（2026-09-01 实测拉取）",
          "version": "v2.10.5-lts（2026-08-06 发布，latest 标签）；需要 Docker（Compose 可选）；镜像 4.62GB（2026-09-01 daocloud 实测）",
          "install_cmds": [
            "# 国内网络：走 daocloud 加速前缀（2026-09-01 实测可拉）\ndocker run -d --name=maxkb --restart=always -p 8080:8080 \\\n  -v ~/.maxkb:/opt/maxkb \\\n  docker.m.daocloud.io/1panel/maxkb:latest\n# 官方原版（网络允许直连 Docker Hub 时）\n# docker run -d --name=maxkb --restart=always -p 8080:8080 -v ~/.maxkb:/opt/maxkb 1panel/maxkb"
          ],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-09-01)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-09-01",
        "ttl_due": "2027-02-28",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-local-rag",
        "agent": "https://trove.xin/agent/combos/combo-local-rag"
      },
      "install_script": "https://trove.xin/combos/combo-local-rag.sh"
    },
    {
      "id": "combo-memory-session",
      "title": "单会话记忆治理栈（claude-mem + deepcode + code-review-graph）",
      "scenario": "用户想要「记得住 + 查得准 + 看得全」的单会话开发闭环——跨会话记忆、提交前静态分析、改动影响面图谱一次装齐。",
      "status": "verified",
      "title_en": "Combo Draft: Memory & Session Governance Stack (claude-mem + deepcode + code-review-graph)",
      "scenario_en": "User wants a single-session dev loop that “remembers + finds precisely + sees everything” — cross-session memory, pre-commit static analysis, change-impact graphs in one bundle.",
      "recorded": "2026-08-29",
      "solves": [
        "claude-mem-6",
        "claude-mem-3"
      ],
      "members": [
        {
          "name": "claude-mem",
          "role": "跨会话记忆底座",
          "download": "官方 npm（包名 claude-mem；插件本体 GitHub release tag 同步）；国内用 npmmirror registry",
          "version": "[13.16.1, 13.16.1]（manifest 锁定插件本体版本；npm CLI 版本号滞后为 12.4.7，属官方现象；worker 运行时需 Bun）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem\nclaude-mem install --ide claude-code   # 预期输出: Installation Complete（Plugin dir: ~/.claude/plugins/...；Worker port: 37700）"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "deepcode",
          "role": "静态分析",
          "download": "官方 PyPI（包名 deepcode-hku）；国内建议清华镜像（PyPI 官方 16s 边缘易超时）",
          "version": "[2.1.0, 2.1.0]（manifest 锁定；Python >= 3.12）",
          "install_cmds": [
            "pip install deepcode-hku --index-url https://pypi.tuna.tsinghua.edu.cn/simple\ndeepcode --version   # 预期输出: DeepCode 2.1.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "code-review-graph",
          "role": "审查图谱",
          "download": "官方 PyPI（包名 code-review-graph，==2.3.8）；国内必须带清华镜像",
          "version": "[2.3.8, 2.3.8]（manifest 锁定；Python >= 3.10）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple code-review-graph==2.3.8\ncode-review-graph --version   # 预期输出: code-review-graph 2.3.8"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-28)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-memory-session",
        "agent": "https://trove.xin/agent/combos/combo-memory-session"
      },
      "install_script": "https://trove.xin/combos/combo-memory-session.sh"
    },
    {
      "id": "combo-mirror-setup",
      "title": "国内网络一键配源",
      "scenario": "新装一台机器，pip/npm/go/docker 全要配国内源；每篇教程各讲各的，容易配错、配完不知道有没有生效",
      "status": "partial",
      "title_en": "Combo: China Network Mirror One-Shot Setup",
      "scenario_en": "Fresh machine needs pip/npm/go/docker mirrors for the CN network; each tutorial covers one tool, easy to misconfigure and hard to verify it took effect.",
      "recorded": "2026-08-29",
      "solves": [
        "pip-mirror-source",
        "npm-mirror-registry",
        "go-proxy-cn",
        "github-direct-connect-fail"
      ],
      "members": [
        {
          "name": "pip",
          "role": "清华 PyPI 镜像",
          "download": "配置写入 `~/.config/pip/pip.conf`（用户级，无需 root；官方源地址 https://pypi.org/simple 为默认，不下载任何东西）",
          "version": "pip >= 21（旧版 pip 对 index-url 支持一致，无硬性要求）",
          "install_cmds": [
            "pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple\npip3 config set global.trusted-host pypi.tuna.tsinghua.edu.cn\n# 验证：读取当前生效配置\npip3 config list"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "npm",
          "role": "npmmirror 镜像",
          "download": "配置写入 `~/.npmrc`（用户级；官方源 https://registry.npmjs.org/ 为默认）",
          "version": "npm >= 7（`npm config set` 行为一致，无硬性要求）",
          "install_cmds": [
            "npm config set registry https://registry.npmmirror.com\n# 验证：应输出 https://registry.npmmirror.com/\nnpm config get registry"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Go",
          "role": "goproxy.cn",
          "download": "配置写入 `~/.config/go/env`（用户级，`go env -w` 官方机制；默认代理 https://proxy.golang.org 国内不可直连）",
          "version": "Go >= 1.13（模块代理机制自 1.13 起为默认）",
          "install_cmds": [
            "go env -w GOPROXY=https://goproxy.cn,direct\n# 验证：应输出 https://goproxy.cn,direct\ngo env GOPROXY"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Docker",
          "role": "daemon.json registry-mirrors",
          "download": "配置写入 `/etc/docker/daemon.json`（系统级，需 root；改动后重启 daemon，**影响运行中容器**，属可选步骤）",
          "version": "Docker Engine >= 20.10（registry-mirrors 字段长期支持）",
          "install_cmds": [
            "# 已有 daemon.json 时先备份再合并，不要覆盖原有配置\nsudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak 2>/dev/null || true\nsudo tee /etc/docker/daemon.json <<'EOF'\n{\n  \"registry-mirrors\": [\"https://docker.1ms.run\", \"https://docker.m.daocloud.io\"]\n}\nEOF\nsudo systemctl restart docker\n# 验证：输出配置中的 mirror 地址\ndocker info | grep -A 2 \"Registry Mirrors\""
          ],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-mirror-setup",
        "agent": "https://trove.xin/agent/combos/combo-mirror-setup"
      },
      "install_script": "https://trove.xin/combos/combo-mirror-setup.sh"
    },
    {
      "id": "combo-model-access",
      "title": "多模型接入与成本可观测",
      "scenario": "项目接了多个模型供应商（OpenAI/Anthropic/国产模型），客户端各配各的 key、成本没人说得清，切换与限流全靠手改",
      "status": "partial",
      "title_en": "Combo Draft: Unified Model Access & Observability",
      "scenario_en": "Project uses multiple model providers (OpenAI/Anthropic/domestic models); clients each configure their own keys, costs are unclear, switching and rate limits are manual.",
      "recorded": "2026-08-29",
      "solves": [
        "ollama-connect-failed",
        "open-webui-ollama-connect"
      ],
      "members": [
        {
          "name": "litellm",
          "role": "统一接入网关",
          "download": "PyPI `litellm[proxy]`（官方；国内清华镜像）",
          "version": ">= 1.98.0（2026-08-29 实测；Python >= 3.8）",
          "install_cmds": [
            "python3 -m venv ~/.litellm-venv\n~/.litellm-venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple litellm[proxy]"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "logfire",
          "role": "可观测",
          "download": "PyPI `logfire`（官方；国内清华镜像）",
          "version": "== 4.41.0（2026-08-28 收录实测版本）",
          "install_cmds": [
            "pip install -i https://pypi.tuna.tsinghua.edu.cn/simple logfire==4.41.0\nlogfire --version"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-model-access",
        "agent": "https://trove.xin/agent/combos/combo-model-access"
      },
      "install_script": "https://trove.xin/combos/combo-model-access.sh"
    },
    {
      "id": "combo-multi-agent-workflow",
      "title": "多 Agent 工作流编排",
      "scenario": "想让多个 Agent 自动协作完成多步任务（并行跑 / 分步走 / 结果汇总），不知道用什么工具",
      "status": "partial",
      "title_en": "Combo: Multi-Agent Workflow Orchestration (ruflo + claude-mem + claude-code-router)",
      "scenario_en": "You want several agents to work through a multi-step task together (run in parallel, step by step, then merge results) and don't know which tools to pick.",
      "recorded": "2026-08-30",
      "solves": [
        "agent-toolchain-coexist",
        "claude-code-router-3"
      ],
      "members": [
        {
          "name": "ruflo",
          "role": "编排主线",
          "download": "npm 包 `ruflo`（官方 npm；国内 npmmirror 前缀）",
          "version": "3.38.20（2026-08-28 收录实测；Node >= 18，需已装 Claude Code / Codex 客户端）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npx -y ruflo init --no-signup --no-skills-sh --no-codex-detect\nnpx -y ruflo --version"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "claude-mem",
          "role": "跨会话记忆支撑",
          "download": "npm 包 `claude-mem`（官方 npm；国内 npmmirror 前缀）",
          "version": "0.11.x（2026-08-28 收录实测；Node >= 18）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g claude-mem\nclaude-mem install --ide claude-code"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "claude-code-router",
          "role": "模型路由支撑",
          "download": "npm 包 `@musistudio/claude-code-router`（官方 npm；国内 npmmirror 前缀）",
          "version": "3.0.22（2026-08-28 收录实测；Node >= 18）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g @musistudio/claude-code-router --no-audit --no-fund\nccr --help"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-30)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-30",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-multi-agent-workflow",
        "agent": "https://trove.xin/agent/combos/combo-multi-agent-workflow"
      },
      "install_script": "https://trove.xin/combos/combo-multi-agent-workflow.sh"
    },
    {
      "id": "combo-permission-security",
      "title": "权限与安全排查（Permission denied / EACCES / 属主不一致）",
      "scenario": "工具装好了但一跑就报权限错——Docker 容器读写挂载目录被拒、git 报 dubious ownership、npm 全局安装 EACCES、SSH 私钥权限过宽、Codex 沙盒 setup refresh 报错",
      "status": "partial",
      "title_en": "Combo Draft: Permission & Security Troubleshooting (Permission denied / EACCES / owner mismatch)",
      "scenario_en": "Tools installed but permission errors on first run — Docker mount dirs rejected, git dubious ownership, npm global EACCES, SSH keys too open, Codex sandbox setup refresh errors.",
      "recorded": "2026-08-29",
      "solves": [
        "ssh-key-permissions",
        "script-permission-denied",
        "git-dubious-ownership",
        "npm-global-eacces"
      ],
      "members": [
        {
          "name": "通用定位",
          "role": "先分清「权限错」与「环境错」",
          "download": "无（诊断方法，非工具）",
          "version": "无",
          "install_cmds": [
            "# 权限错的特征：报错关键字 Permission denied / EACCES / too open / dubious ownership；\n  # 与「环境错」（daemon 不可达 / 端口冲突，见 combo-docker-wsl-env）区分开，走不同排查链。\n  # 先看工具自己的日志定位（Codex 沙盒示例，Windows 侧）：\n  #   C:\\Users\\<user>\\.codex\\.sandbox\\sandbox.<date>.log\n  # 本环境实证：C:\\Users\\<user>\\.codex\\.sandbox\\ 下存在按日期命名的多份沙盒日志"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Docker 容器",
          "role": "uid 对齐",
          "download": "Docker Engine/Docker Desktop（见 combo-docker-wsl-env）",
          "version": ">= 20.10（与 combo-docker-wsl-env 一致）",
          "install_cmds": [
            "# 容器 uid 对齐宿主（推荐）：\n  docker run -u \"$(id -u):$(id -g)\" -v \"$PWD/data:/data\" ...\n  # compose：user: \"${UID}:${GID}\"（${UID} 需在 shell 里导出）\n  # 一次性修正宿主目录属主：\n  sudo chown -R 1000:1000 ./data\n  # 临时应急（破坏最小权限原则，仅本机调试）：\n  docker run -u 0:0 ..."
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Git",
          "role": "safe.directory 白名单",
          "download": "无（git 自带配置）",
          "version": "git >= 2.35（白名单报错提示机制；本机实测 2.53.0）",
          "install_cmds": [
            "# 白名单按目录登记、不递归——同盘其他仓库还要再 add：\n  git config --global --add safe.directory <仓库绝对路径>\n  # 仓库很多时才考虑全局信任（共享机器不推荐）：\n  git config --global --add safe.directory '*'\n  # Linux 文件系统可直接改属主（NTFS/exFAT 挂载盘无效，见下方 drvfs 警告）：\n  sudo chown -R $(id -u):$(id -g) <仓库路径>"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "npm",
          "role": "全局 prefix 改到用户目录",
          "download": "无（npm 自带配置）",
          "version": "无（任意 npm 版本；本机 node v22.22.1，npm prefix 默认 /usr/local——正是 EACCES 高发默认值）",
          "install_cmds": [
            "# 形态 1（推荐，一劳永逸）：\n  npm config set prefix ~/.npm-global\n  echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.bashrc\n  # 形态 2：把 ~/.npm 所有权还给自己：\n  sudo chown -R $(whoami) ~/.npm\n  # 国内网络：npm 全局包走 npmmirror（见 combo-mirror-setup）：\n  npm config set registry https://registry.npmmirror.com"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "SSH",
          "role": "私钥权限收紧",
          "download": "无（OpenSSH 自带）",
          "version": "无",
          "install_cmds": [
            "# Linux/macOS/WSL 侧：\n  chmod 600 ~/.ssh/id_ed25519    # 私钥\n  chmod 644 ~/.ssh/*.pub         # 公钥\n  chmod 700 ~/.ssh               # 目录\n  # Windows 原生 OpenSSH（chmod 不适用）：\n  icacls \"私钥路径\" /inheritance:r /grant:r \"%USERNAME%:R\"\n  # WSL 访问 /mnt/c 上的密钥：drvfs 不支持 chmod（实测 chmod 600 后 ls 仍 777）——\n  # 复制到 WSL 内再 chmod，或在 Windows 侧用 icacls 修"
          ],
          "notes": [],
          "guide": false
        },
        {
          "name": "Codex 沙盒",
          "role": "日志定位 + 属主/ACL 修复",
          "download": "无（Codex 自带）",
          "version": "无",
          "install_cmds": [
            "# 1) 先看日志定位（比瞎猜快），搜 setup refresh had errors / write ACE：\n  #    C:\\Users\\<user>\\.codex\\.sandbox\\sandbox.<date>.log（旧版路径 .codex.sandbox）\n  # 2) 改 owner（最常见修复）：\n  takeown /f <目录> /r /d y\n  # 3) 补 ACL（owner 正确仍缺权限时）：\n  icacls <目录> /grant <user>:(OI)(CI)F /T\n  # 4) 验证：重开会话触发 setup refresh，日志 errors=[] 即恢复（幂等，无需重装）"
          ],
          "notes": [],
          "guide": false
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "partial",
        "validated_at": "2026-08-29",
        "ttl_due": "",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-permission-security",
        "agent": "https://trove.xin/agent/combos/combo-permission-security"
      },
      "install_script": "https://trove.xin/combos/combo-permission-security.sh"
    },
    {
      "id": "combo-security-preflight",
      "title": "安全前置（agentshield + skillspector）",
      "scenario": "用户在装任何插件/技能/仓库之前想先跑一遍安检——配置面攻击面审计 + 技能/内容面扫描，两个只读扫描器一次装齐。",
      "status": "verified",
      "title_en": "Combo Draft: Security Preflight (agentshield + skillspector)",
      "scenario_en": "User wants a security preflight before installing any plugin/skill/repo — config attack-surface audit + skill/content scan, two read-only scanners in one bundle.",
      "recorded": "2026-08-29",
      "solves": [
        "ssh-key-permissions",
        "script-permission-denied",
        "execpolicy-restricted"
      ],
      "members": [
        {
          "name": "skillspector",
          "role": "技能/仓库内容安检",
          "download": "官方 GitHub Release wheel（v2.10.0，skillspector-2.10.0-py3-none-any.whl，476KB）；**注意：不在 PyPI**（PyPI/清华镜像均 404，pip install skillspector 必失败）——发行渠道 = GitHub Release wheel，国内走 gh-proxy 下载 wheel、依赖走清华镜像",
          "version": "[2.10.0, 2.10.0]（manifest 锁定；Python 3 任意版本 wheel）",
          "install_cmds": [
            "curl -sL -o skillspector-2.10.0-py3-none-any.whl https://gh-proxy.com/https://github.com/NVIDIA/SkillSpector/releases/download/v2.10.0/skillspector-2.10.0-py3-none-any.whl\npip install -i https://pypi.tuna.tsinghua.edu.cn/simple skillspector-2.10.0-py3-none-any.whl\nskillspector --version   # 预期输出: 2.10.0"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "agentshield",
          "role": "agent 配置攻击面审计",
          "download": "官方 npm（包名 ecc-agentshield）；国内用 npmmirror registry；GitHub release v1.4.0 无资产，官方分发走 npm",
          "version": "[1.5.0, 1.5.0]（manifest 锁定；Node >= 18；CLI 二进制名 agentshield，包名 ecc-agentshield）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g ecc-agentshield\nagentshield scan   # 预期输出: AgentShield Security Report"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-28)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-security-preflight",
        "agent": "https://trove.xin/agent/combos/combo-security-preflight"
      },
      "install_script": "https://trove.xin/combos/combo-security-preflight.sh"
    },
    {
      "id": "combo-web-deploy-verify",
      "title": "网页部署验证",
      "scenario": "网站部署/更新后无法确认「网页真实可访问」——部署脚本说成功，但页面白屏、路由 404、样式没加载，只能靠人打开浏览器看",
      "status": "verified",
      "title_en": "Combo Draft: Web Deploy Verification",
      "scenario_en": "Can't confirm a site is truly reachable after deploy/update — scripts say success, but pages blank-screen, routes 404, styles fail; only a human opening a browser catches it.",
      "recorded": "2026-08-29",
      "solves": [
        "vite-chunkloaderror",
        "vite-history-fallback",
        "css-load-failure"
      ],
      "members": [
        {
          "name": "vite-deploy",
          "role": "前端构建",
          "download": "npm 包 `vite`（条目名 vite-deploy，官方；国内 npmmirror 前缀）",
          "version": "5.0~5.4（2026-08-29 收录实测 5.4.21）",
          "install_cmds": [
            "npm install --save-dev vite --registry=https://registry.npmmirror.com\nnpx vite build"
          ],
          "notes": [],
          "guide": true
        },
        {
          "name": "playwright-mcp",
          "role": "浏览器验证",
          "download": "npm 包 `@playwright/mcp`；免安装 `npx -y @playwright/mcp@latest`",
          "version": ">= 0.0.79（2026-08-29 实测；Node >= 18）",
          "install_cmds": [
            "npm_config_registry=https://registry.npmmirror.com npm install -g @playwright/mcp\nnpx --no-install playwright install chromium"
          ],
          "notes": [],
          "guide": true
        }
      ],
      "evidence": "Evidence details kept locally (2026-08-29)",
      "verification": {
        "state": "verified",
        "validated_at": "2026-08-29",
        "ttl_due": "2027-02-25",
        "ttl_days": 180
      },
      "pages": {
        "human_zh": "https://trove.xin/combos/combo-web-deploy-verify",
        "agent": "https://trove.xin/agent/combos/combo-web-deploy-verify"
      },
      "install_script": "https://trove.xin/combos/combo-web-deploy-verify.sh"
    }
  ]
}