SKILL.md 32 KB


name: skill-heterarchy description: "预计超 3 步或 2 分钟的任务,先拆并行维度派 FmodeCode/Claude Code。主控写任务书+验收,不阻塞。" version: 1.2.0 author: Yuyang001 (FmodeAgent) license: MPL-2.0 copyright: "Copyright (c) 2026 未来飞马 Fmode"

tags: [未来飞马, 智能体技能, 超级技能, 系统级, 平台基础设施, FmodeAgent, Hermes Agent, FmodeCode, Claude Code, heterarchy, dispatch, cognitive-collaboration, parallel, parallel-batch-orchestration, batch-orchestration, topological-sort, async-receipt, timeout-fallback, cdn-verification, obs-deploy, progress-board]

skill-heterarchy — 对内分思实操技能

Delegate 是对外派活,Heterarchy 是对内分思。 本技能用 FmodeAgent / Hermes Agent 的底层工具在单一 Agent 内部分化多心智并行思考。 加载方式:skill_view(name='skill-heterarchy')


〇、FmodeAgent / Hermes Agent 工具并行调用机制(并行调用铁律)

每次 FmodeAgent / Hermes Agent 处理用户消息时,可以在同一轮对话内同时发起多个独立的工具调用。这是 Heterarchy 能真正"并行"的底层前提——如果连工具调用都串行发,后面的并行派发就是空谈。

0.1 同轮并发的能力组合

场景 同一轮并发发出
多路信息采集 多个 terminal()(不同命令)
边读边搜 read_file() + search_files()
边调研边干活 web_search() + 一个后台 FmodeCode / Claude Code 启动
多进程编排 多个 tool_call() 管理不同进程

0.2 铁律:独立无依赖 → 必须并发

原则:两个工具调用之间若没有数据依赖(后者不需要前者的输出),就必须放在同一轮发出,而不是分成多轮对话串行。

反例(串行 3 轮对话,浪费 3 个往返):

第 1 轮:read_file(A)          → 等结果
第 2 轮:read_file(B)          → 等结果
第 3 轮:web_search(...)       → 等结果

正例(一轮搞定,延迟≈最慢的那一个):

第 1 轮:read_file(A) + read_file(B) + web_search(...)   ← 同轮并发

0.3 判断方法

在发工具调用前,对每个待发调用问一句:

"我这次调用需要用到另一个调用的输出吗?"

  • 不需要 → 并入本轮,并发发出。
  • 需要 → 才拆到下一轮,等前置返回。

0.4 与 Heterarchy 的衔接

本机制是后续所有章节(§X 批量分析、§Y 进度表、§Z 异步回执、§V 拓扑分批)的地基

  • 认知拆解(§一)判定出的"无依赖维度",靠本章的并发机制真正并行落地;
  • FmodeCode / Claude Code 派发(§三)多个 terminal(background=True, notify=True) 必须在同一轮发出,才能同时启动多个心智;
  • 一旦发现有依赖,才退回 §V 的拓扑分批。

一句话:先把能并的都并出去,再把剩下的按依赖排序。


零、何时触发本技能(★ 硬门槛,先算账再开工)

本节 1.2.0 起改为硬规则。原因:旧版只写了「当你收到一个复杂任务, 明显可以拆成多个独立维度并行处理时」——判断太软,忙碌的 Agent 会直接跳过, 于是长任务串行硬啃、用户干等。触发条件必须是可自检的是非题,不是感觉。

0.1 开工前 10 秒自检(任一为「是」→ 必须先加载本技能再动手)

# 自检问题
1 预计需要 > 8 个工具调用 → 拆
2 预计有任何一次等待 > 30 秒(安装/构建/克隆/遍历)? → 拆
3 目标是 N 个同类对象(N ≥ 3)?如「所有容器」「几十个生命」「批量安装」 → 拆
4 任务含 ≥ 2 个可独立完成的维度(如「改代码」+「改文档」+「写测试」)? → 拆
5 用户说了 批量 / 全部 / 所有 / 几十个 / 并发 / 尽快 → 拆

判定纪律:不确定就按「拆」处理。 拆错的代价只是多写一份任务书; 不拆的代价是用户干等几十分钟、主控上下文被中间数据淹没。

0.2 反例(真实事故,2026-09-23,运维001)

任务:给 34 台数字生命容器批量铺 3 个技能 + 改云函数 + 建验收机制
实际:整轮串行自己啃 —— 47 轮工具调用,单条命令 60/90/120 秒地等,
      期间用户在聊天里反复问「怎么还没反应」,主控上下文涨到 129k。
本应:拆成 4 个认知子单元并行派 Claude Code(技能改造 / 容器分发 /
      云函数部署 / 验收器开发),主控只写任务书 + 验收。
教训:任务明明命中自检 1/2/3/5 四条,却因为「触发条件是软描述」而没被拦住。

0.3 触发词(出现即算命中)

「多个主题需要完成」「同时做这几件事」「这几个部分需要…」「尽量并行」 「批量」「全舰队」「所有容器」「几十个生命」「铺开」「逐台」

0.4 何时不用(明确排除,避免滥用)

  • 单一步骤命令 → 直接 terminal()execute_code()
  • 纯对外输出(发一条消息、写一个简单文件)→ 直接做
  • 需要跨 Profile 协作 → 用 kanban_create 委派,不是 Heterarchy
  • 任务本身不可分割(如「把这一行配置改掉」)→ 直接做

一、任务拆解(认知分化 — FmodeAgent / Hermes Agent 自己做)

这是唯一不能交给 FmodeCode / Claude Code 的步骤。FmodeAgent / Hermes Agent 必须亲自:

  1. 理解用户真实需求
  2. 识别可并行的独立维度(每个维度 = 一个独立认知子单元)
  3. 判断子单元之间是否有依赖关系
  4. 确定项目工作目录(每个 FmodeCode / Claude Code 进程必须在正确的 git 仓库根目录启动)

实操模板(FmodeAgent / Hermes Agent 的思考过程):

收到:"四个主题的 HTML PPT 需要图文并茂增强"

拆解:
├→ 主题一:数字分身(独立 → 无前置依赖)
├→ 主题二:Harness(独立 → 无前置依赖)
├→ 主题三:Loop(独立 → 无前置依赖)
├→ 主题四:RSI(独立 → 无前置依赖)
└→ 依赖:无(四主题可完全并行)

项目根目录:/opt/data/git-repos/skill-present
FmodeCode / Claude Code 必须在项目根目录启动,否则读不到 CLAUDE.md 的项目规则。

二、任务书编写(心智分化)

每个认知子单元需要一份独立的任务书,包含:

1. 背景上下文(用户原始需求)
2. 本次要做什么(明确范围,不要多做)
3. 要保留什么(已有的内容别动)
4. inline 素材(路径、关键描述)
5. 纪律约束:
   - 框架不可改
   - 自绘 SVG 保留
   - 案例 CDN 图优先用已有
   - skill-image 只用于核心架构图/场景图
6. 验收标准(git commit、Content-Type 验证、URL 200)
7. 禁止事项(不要重写、不要改标题、不要改结构)

任务书写入临时文件 /tmp/cc-xxx-task.md,用 cat 管道喂入 FmodeCode / Claude Code,不用 $(cat) 避免路径问题:

正确姿势:

cat /tmp/cc-xxx-task.md | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > /tmp/cc-xxx.log 2>&1 &

错误姿势(踩过坑):

# 不要用 $(cat file) —— 文件路径在后台进程可能不同
# 不要省略绝对路径 —— exit 127 找不到 claude

三、FmodeAgent / Hermes Agent 协同工具调用表(核心实操)

3.1 启动心智 — terminal + background + notify

# FmodeAgent / Hermes Agent 代码中:
terminal(
    command=f"""
    export PATH="/opt/data/npm-global/bin:$PATH"
    cd {project_root}
    cat {task_file_path} | /opt/data/npm-global/bin/claude \\
      -p --dangerously-skip-permissions \\
      > {log_path} 2>&1
    """,
    background=True,
    notify=True,
    timeout=1800,
    workdir=project_root
)
# → 返回 session_id 用于后续监控

3.2 阻塞监控 — process_manage + sleep

# 启动后 60s 首次健康检查
terminal("sleep 60 && pgrep -f 'claude -p' | wc -l", timeout=90)

# 或主动轮询
tool_call(calls=[{"name": "process_manage", "arguments": {"action": "list"}}])
# → 检查 uptime_seconds 是否在增长
# → 检查 output_preview 是否为空

3.3 心智收敛 — git log + curl Content-Type

# 检查是否 commit
terminal("cd {repo} && git log --oneline -3", timeout=10)

# 验证图片 Content-Type(重要!只查 HTTP 200 会被 404 首页骗)
terminal(f"""
for url in $(grep -roh 'https://fmode.cn[^"'"'"' )]*\\.png' {repo}/*.html 2>/dev/null | sort -u); do
  CT=$(curl -s -m 10 -o /dev/null -w "%{{content_type}}" "$url")
  [[ "$CT" == image/* ]] || echo "❌ 图裂: $CT $url"
done""", timeout=30)

3.4 横向协商(可选)— 前一子单元的产物成为后一子单元的输入

# 如果主题一的 commit 包含架构图,主题二需要引用
terminal("cd {repo} && git diff HEAD~1 --name-only", timeout=10)
# 读文件内容作为 inline 素材塞进主题二的任务书
read_file(path="{repo}/path/to/architecture.md")

3.5 模型配置检查

# 检查 settings.json 确保模型名正确
# 用户钦点:FmodeAgent / Hermes Agent=deepseek/deepseek-v4.1-flash, FmodeCode / Claude Code=deepseek/deepseek-v4.1-flash[1m]
terminal("cat {project_root}/.claude/settings.json", timeout=5)
# 如发现模型名不对,patch 修正

四、派发策略决策树

收到复杂任务
│
├→ 可以拆成 2-5 个独立维度?
│   ├→ 否 → 单一 FmodeCode / Claude Code 进程处理 或 本 Agent 直接完成
│   └→ 是 → 继续
│
├→ 子单元之间有依赖关系?
│   ├→ 是 → 拓扑排序:先做无依赖的,串行分批
│   │   ├→ 所有独立 → 全并行(最多 5 个同时)
│   │   └→ 部分依赖 → 分批并行:依赖关系跨批次传递
│   └→ 否 → 全并行启动
│
├→ 每个子单元的工作量?
│   ├→ 简单(< 50 行代码/单页)→ 不启 FmodeCode / Claude Code,本 Agent 直接写
│   ├→ 中等(需要完整思考/调研/创作)→ 独立 FmodeCode / Claude Code 进程
│   └→ 复杂(需要跨文件多模块/项目级)→ 优先用 kanban_create 委派给 fullstack profile
│
├→ 项目目录正确吗?
│   ├→ 每个 FmodeCode / Claude Code 必须在项目根目录启动
│   └→ 错目录 → FmodeCode / Claude Code 读不到 CLAUDE.md → 规则丢失 → 出次品
│
└→ 启动后:
    ├→ 60s 后检查存活
    ├→ 存活 → 等待 notify → 收敛验收
    └→ 全死 → 执行恢复流程

五、恢复流程(阻塞自愈 — 实操版)

一旦 process_manage(action="list") 显示所有进程已退出,按以下顺序处理:

5.1 查日志尾巴

terminal(f"tail -30 {log_path}", timeout=10)

5.2 根据错误分类处理

日志症状 诊断 处置
command not found: claude 或 exit 127 claude 不在 PATH /opt/data/npm-global/bin/claude 绝对路径
503 No available channel 或 exit 1 模型渠道不可用 检查 ~/.claude/settings.json 模型名(少 deepseek/ 前缀会 503)
401Not logged in Token 失效 检查 ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY
日志为空 + exit 0 假成功 $(cat)cat pipe方式重派
日志有内容但 exit 1 任务执行中出错 读日志末尾判断:模型退火 / 文件读写错 / 权限问题
通知没来、长时间无响应 进程阻塞 pgrep -f claude 如果还在跑则等待;否则杀进程重派

5.3 重派策略

# 1. 如果是"续做"(之前的部分产物可用)
#    在新的任务书开头写明:
#    "已完成:xxx(state) 继续做:yyy(未完成部分)"

# 2. 如果是"从零重做"(完全失败)
#    缩小范围:大任务拆小
#    禁用 FmodeCode / Claude Code 的某些能力:机器产出内容需要再确认

# 3. 连败 3 次同域 → 停止 FmodeCode / Claude Code 重试
#    本 Agent 直接处理 或 拆成更细的原子指令

六、完整实操流程(以 V6 四主题图文增强为例)

Step 1: 认知拆解
FmodeAgent / Hermes Agent 消化用户需求:"四个主题需要图文并茂增强,框架不动,加图"
→ 四主题可完全并行,项目根目录=skill-present

Step 2: 心智分化  
FmodeAgent / Hermes Agent 为每个主题编写独立任务书(/tmp/cc-v6-0x-resume.md)
每份包含:当前 commit hash + 结构框架 + 纪律 + 验收标准

Step 3: 并行启动
terminal(background=True, notify=True, workdir=skill-present,
  command=f"cat /tmp/cc-v6-01-resume.md | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > /tmp/cc-v6-01-r2.log 2>&1")
terminal(background=True, notify=True, workdir=skill-present,
  command=f"cat /tmp/cc-v6-02-resume.md | ...")  # 主题二
terminal(background=True, notify=True, workdir=skill-present,
  command=f"cat /tmp/cc-v6-03-resume.md | ...")  # 主题三
terminal(background=True, notify=True, workdir=skill-present,
  command=f"cat /tmp/cc-v6-04-resume.md | ...")  # 主题四

Step 4: 阻塞自愈(首次 60s 后)
process_manage(action="list") → 检查所有子单元存活

Step 5: 心智收敛(逐个子单元 notify)
每个 notify 到达:
  └→ git log --oneline 检查 commit →
  └→ curl Content-Type 验证图片 →
  └→ 记录产出摘要

Step 6: 统一交付
push 所有 commit → OBS sync → CDN 刷新 → URL 验证 → 发用户

七、常见陷阱(踩坑记录)

7.1 任务书喂入方式

  • claude -p "$(cat task.md)" → 路径丢失,后台进程找不到文件
  • cat task.md | claude -p → 管道直送,无文件路径依赖

7.2 项目根目录

  • ❌ 在父目录启动 FmodeCode / Claude Code → 读不到 .claude/rules/
  • ✅ 必须在 git 项目根目录启动(读得到 CLAUDE.md + rules + settings.json)

7.3 图片验证只看 200

  • curl -o /dev/null -w "%{http_code}" → 200 可能是 404 首页
  • curl -o /dev/null -w "%{content_type}" → 必须是 image/*

7.4 版本号

  • ❌ 跳 1.0.0、5.0.0 大版本
  • ✅ 0.0.1 → 0.0.12 → 0.0.99 → 0.1.1 小步迭代

7.5 配置 files 字段

  • ❌ 不设 files: ["lib/", "bin/", ...] → npm publish 把无关文件全打包
  • ✅ 精确声明要发布的文件

八、技能与本 Agent 内置工具的关系

本技能不替代 FmodeAgent / Hermes Agent 内置工具,而是编排它们:

FmodeAgent / Hermes Agent 收到任务
│
├→ skill-heterarchy(本技能)→ 决策是否启用异层认知
│   └→ 认知分化 → 写任务书
│
├→ terminal(FmodeAgent / Hermes Agent 工具)→ 后台启动 FmodeCode / Claude Code 进程
├→ process_manage → 监控存活
├→ read_file / patch / git → 收敛验证
├→ web_search → 前置调研
└→ delegate_task → 轻量子任务(与 Heterarchy 可组合使用)

何时用 delegate_task 而不是 Heterarchy?

  • 子任务是"帮我查点东西"(轻量检索)→ delegate_task
  • 子任务是"完整思考 + 产出可交付物" → Heterarchy + FmodeCode / Claude Code

何时用 kanban_create 而不是 Heterarchy?

  • 任务需要跨 Profile 的独立 MCP 环境 → kanban_create
  • 任务在本 Agent 的能力范围内,只是需要并行 → Heterarchy

X、execute_code 批量分析模式

当要对多个文件/多条数据做 grep、统计、聚合、转换时,不要用一串 terminal() 逐个敲。改用 FmodeAgent / Hermes Agent 内置的 execute_code(Python 内核),一次进程内跑完。

X.1 适用场景

场景 例子
跨目录 grep + 聚合 统计所有技能里各模型名的出现次数
多 HTTP 请求并行测试 一次性 curl 全部产物 URL 并汇总 Content-Type
批量文件读写 + 统计 扫描所有 .html,统计图片引用数
数据格式转换 JSON ↔ CSV ↔ YAML 互转
git diff 分析 汇总一次 commit 涉及的文件类型/行数

X.2 原则

超过 2 个文件需要 grep / 统计 / 聚合 → 用 execute_code,而不是逐个 terminal

逐个 terminal 的代价:每个文件一轮往返 + 一次进程启动开销;execute_code 把这些压成一次调用、一个摘要

X.3 模板

import os, json, subprocess, glob

# 1. 收集目标
targets = glob.glob("/opt/data/git-repos/*/SKILL.md")

# 2. 批量处理
summary = []
for t in targets:
    try:
        with open(t, encoding="utf-8") as f:
            text = f.read()
        summary.append({
            "path": t,
            "lines": text.count("\n") + 1,
            "has_version": "version:" in text,
        })
    except Exception as e:
        summary.append({"path": t, "error": str(e)})

# 3. 只打印结果摘要(不要 print 原始大文本,会污染上下文)
print(f"扫描 {len(summary)} 个文件")
print(json.dumps([s for s in summary if s.get("error")], ensure_ascii=False, indent=2))
print("缺失 version 的文件数:", sum(1 for s in summary if not s.get("has_version")))

X.4 纪律

  • 只 print 摘要,不要把整个文件内容倒进上下文——那等于没省。
  • 需要逐条证据时,print 结构化 JSON(json.dumps(..., indent=2))而不是裸文本。
  • 网络类批量任务用 concurrent.futures.ThreadPoolExecutor 并发,不要 for 循环串行 curl。
  • execute_code 适合确定性脚本活;需要"整体思考/创作"的部分仍走 §三 的 FmodeCode / Claude Code 派发。

Y、多任务进度表(状态看板)

FmodeAgent / Hermes Agent 在同时推进多个 FmodeCode / Claude Code 后台任务时,每次回复用户前先构造一张状态表,让用户一眼看清全局。不要只说"还在跑"。

Y.1 状态看板模板

# 每次回复用户前构造(在 execute_code 或直接拼字符串)
tasks = [
    ("主题一 · 数字分身", "running",  "3m12s"),
    ("主题二 · Harness",  "done",     "4m05s"),
    ("主题三 · Loop",     "failed",   "1m47s"),
]

ICON = {"running": "🟡 运行中", "done": "✅ 已完成", "failed": "❌ 失败(重派)"}

rows = "\n".join(
    f"| {name} | {ICON[status]} | {dur} |" for name, status, dur in tasks
)
print(f"""
| 任务 | 状态 | 时长 |
|------|------|------|
{rows}
""")

输出示例:

| 任务 | 状态 | 时长 |
|------|------|------|
| 主题一 · 数字分身 | 🟡 运行中 | 3m12s |
| 主题二 · Harness | ✅ 已完成 | 4m05s |
| 主题三 · Loop | ❌ 失败(重派) | 1m47s |

Y.2 纪律

  • 状态三态:🟡 运行中 / ✅ 已完成 / ❌ 失败(重派)——不要造模糊词。
  • 时长要真实:从 process_manage(action="list")uptime_seconds 或启动时间戳推算,不要拍脑袋。
  • 失败项必须注明下一步动作(重派/续做/放弃),否则看板只是情绪价值。
  • 状态表是给用户的唯一真相源:有它就不必再让用户追问"到哪了"。

Z、主 Session 不阻塞(异步回执模式)

Heterarchy 的核心承诺:主 Session 永远不被某个 FmodeCode / Claude Code 任务卡住。收到请求后立即回执,派完就撒手,靠 notify 回调驱动后续。

Z.1 标准流程

收到用户请求
  ├→ ① 立即发一条回执:「已收到,开始执行,分配 X 个任务同时进行」
  ├→ ② 全 FmodeCode / Claude Code Dispatch:terminal(background=True, notify=True) 全部同轮发出(见 §〇)
  ├→ ③ 不等完成,先回复用户:「任务已派发,等完成通知」
  └→ ④ 当 FmodeCode / Claude Code notify 到达(作为新消息进来),再验证并交付

Z.2 关键点

步骤 要点
① 回执 明确告知拆成几个任务预计并行,让用户安心
② 派发 所有 terminal(background=True, notify=True) 同一轮发出,不要等第一个再发第二个
③ 撒手 派发完立刻回复用户,绝不用前台 sleep 干等
④ 回调 notify 到达 = 一次新的唤醒,此时才 git log / 验证 / 交付

Z.3 反模式

❌ 派发 → 前台 sleep 600 → 查状态 → 再 sleep → ...(主 Session 被阻塞,用户干等)
✅ 派发 → 立即回复 → 挂起 → notify 唤醒 → 验证交付

为什么重要:前台阻塞会占满对话轮次、无法响应用户中途插话、也无法同时推进其它任务。异步回执把"等待"交给事件系统,主 Session 只负责编排


W、超时兜底机制

每个 FmodeCode / Claude Code 任务必须设 timeout=1800(30 分钟)。terminal 前台最大 600s,超过会自动转后台 + notify。

W.1 派发时统一带上超时

terminal(
    command=f"cat {task_file} | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > {log} 2>&1",
    background=True,
    notify=True,
    timeout=1800,          # ← 30 分钟硬上限
    workdir=project_root,
)

W.2 超时处理流程

notify: timeout (exit 124)
  ├→ tail -30 log → 看中途产物
  ├→ 如果已有部分产出:
  │   └→ 写接力任务书(标明已完成部分 + 待做部分)→ 重派
  ├→ 如果零产出(空日志):
  │   └→ 改小任务范围,重新派发
  └→ 连续 2 次超时 → 拆分子任务更小粒度

W.3 诊断命令

# exit 124 = timeout;先看日志尾部和是否已 commit
terminal(f"tail -30 {log_path}", timeout=10)
terminal(f"cd {repo} && git log --oneline -3", timeout=10)   # 有无中途 commit?

W.4 处置矩阵

症状 判断 处置
exit 124 + 日志有产物 大任务未跑完 接力任务书:已完成 X / 待做 Y
exit 124 + 日志空 卡在启动/首轮推理 缩小范围重派,或换更小模型
连续 2 次 exit 124 任务粒度过大 拆成更小原子子任务
日志刷屏无进展 死循环/反复重试 杀进程,改任务书约束(禁某操作)

铁律:超时不等于失败——先看中途产物。有产物就走接力(省时省 token),零产物才重做。


V、拓扑排序(有依赖的任务分批并行)

当子单元之间存在依赖时,不能无脑全并行。先建模依赖图,再分批:无依赖的批次并行,有依赖的批次等前置完成。

V.1 依赖建模

task_a → task_b → task_c   (串行链)
task_a → task_b            (task_b 依赖 task_a)
         task_c            (task_c 独立,与 a/b 无依赖)

V.2 分批并行启动

第 1 批(无依赖):task_a + task_c 同时启动
  → 等 task_a 完成 → 取产出
第 2 批(依赖 task_a):task_b 启动
  → 等 task_b 完成
交付

V.3 执行要点

要点 说明
批内并行 同批任务同一轮发出(§〇),互不等待
批间串行 下一批要等本批关键路径任务完成
跨批传料 前置任务的 commit / 文件作为后置任务书的 inline 素材(§3.4)
关键路径优先 先启动最长依赖链的头,让串行链尽早开跑

V.4 与 §四决策树的衔接

§四的决策树已判定"有依赖 → 拓扑排序,分批并行"。本章是它的落地细则

  • 依赖图为空 → 退化为全并行(第 1 批装下所有任务);
  • 依赖图为链 → 每批只放 1 个,退化为串行,但每批内部仍尽量并发无关任务。

一句话:能并的并、该等的等——用依赖图说话,不靠感觉。


U、结果聚合汇报

当多个 FmodeCode / Claude Code 任务全部完成(或部分失败)后,统一聚合成一张交付汇总表,而不是把 N 个任务的日志碎片甩给用户。

U.1 聚合模板

results = []
for notify in notifies:
    log = read_log(notify.log_path)
    if "commit" in log or success_marker(log):
        results.append(("✅", notify.task_name, notify.url))
    else:
        results.append(("❌", notify.task_name, error_of(log)))

rows = "\n".join(f"| {name} | {mark} | {link} |" for mark, name, link in results)
print(f"""
## 并行任务交付汇总
| 任务 | 结果 | 链接 |
|------|------|------|
{rows}
""")

U.2 纪律

  • 先验证再汇总:每条结果都要经过 git log + URL Content-Type 验证(§T / §P),不能拿"日志看着像成功"充数。
  • 失败项必须带错误 行要写清原因(超时/503/空日志),便于用户决策。
  • 汇总表 = 交付凭证:表里每个 ✅ 都是可点击验证的链接。
  • 部分失败不阻塞交付:先交付成功的,失败的进入 §W/§五 的重派队列,并在表下注明"重派中"。

T、OBS + CDN 部署验证链

FmodeCode / Claude Code 产出 HTML / 图片后,部署链路必须逐环节验证,不能 sync 完就当完事。

T.1 部署链

1. OBS sync → obs://www-fmode.cn/dev/xxx/
2. CDN 刷新(华为云 CDN 缓存清除)
3. 验证所有 URL

T.2 逐 URL 验证

for url in $ARTIFACTS; do
  code=$(curl -m 10 -o /dev/null -w "%{http_code}" "$url")
  ct=$(curl -m 10 -o /dev/null -w "%{content_type}" "$url")
  if [[ "$code" != "200" ]]; then echo "❌ HTTP $code  $url"; continue; fi
  case "$ct" in
    text/html*|image/*|application/*) echo "✅ $code $ct  $url" ;;
    *) echo "❌ 类型异常: $ct  $url" ;;
  esac
done

T.3 Python 版(批量)

import subprocess

def check(url):
    code = subprocess.run(["curl","-m","10","-o","/dev/null","-w","%{http_code}",url],
                          capture_output=True, text=True).stdout
    ct = subprocess.run(["curl","-m","10","-o","/dev/null","-w","%{content_type}",url],
                        capture_output=True, text=True).stdout
    ok = code == "200" and ct.startswith(("text/html","image/","application/"))
    return ok, code, ct

bad = [(u, c, t) for u in artifacts for ok, c, t in [check(u)] if not ok]
print("全部通过" if not bad else f"❌ {len(bad)} 个异常: {bad}")

T.4 纪律

  • CDN 刷新后必须重验:不刷新可能拿到旧缓存,刷新后可能拿到 404——两头都要 curl。
  • 断言而非观察assert resp == 200 / assert ct.startswith(...),不满足就报错,不要"看着还行"。
  • 部署链是串行依赖(sync → 刷新 → 验证),天然适合 §V 的拓扑链:每步是上一步的依赖。

S、FmodeCode / Claude Code 环境自举

每次启动 FmodeCode / Claude Code 前必须确保环境变量齐备,否则轻则 401/503,重则 exit 127。

S.1 必备环境

export PATH="/opt/data/npm-global/bin:$PATH"
export ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY
export ANTHROPIC_BASE_URL=https://api.fmode.cn

内联写进 terminal 命令(推荐,避免 shell 状态不持久):

terminal(
    command=f"""
    export PATH="/opt/data/npm-global/bin:$PATH"
    export ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY
    export ANTHROPIC_BASE_URL=https://api.fmode.cn
    cd {project_root}
    cat {task_file} | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > {log} 2>&1
    """,
    background=True, notify=True, timeout=1800, workdir=project_root,
)

S.2 settings.json 模型名

# 必须写入正确的模型名,少前缀会 503
terminal(f"cat {project_root}/.claude/settings.json", timeout=5)
# FmodeCode / Claude Code 模型:deepseek/deepseek-v4.1-flash[1m]

S.3 自举检查清单

检查项 失败症状 修法
PATH 含 npm-global exit 127 command not found 绝对路径 /opt/data/npm-global/bin/claude
ANTHROPIC_AUTH_TOKEN 401 / Not logged in =$FMODE_API_KEY
ANTHROPIC_BASE_URL 连不上网关 https://api.fmode.cn
settings.json 模型名 503 No available channel 补全 deepseek/ 前缀

纪律:环境设置内联在启动命令里,不要依赖"上一次 export 还在"——后台进程与交互 shell 状态不共享。


R、工作目录纪律

FmodeCode / Claude Code 进程的工作目录决定它能否读到项目规则。目录错了,规则静默丢失,产出必是次品。

R.1 铁律

  • 每个 FmodeCode / Claude Code 进程的 workdir 必须设为 git 项目根目录
  • 错目录 → FmodeCode / Claude Code 读不到 .claude/rules/ / CLAUDE.md / settings.json规则丢失
  • terminal() 内用 cd {workdir} && ... 双重保证workdir 参数 + 命令内 cd)。

R.2 正误对照

# ✅ 双重保证
terminal(
    command=f"cd {project_root} && cat {task_file} | /opt/data/npm-global/bin/claude -p ...",
    workdir=project_root,
    background=True, notify=True,
)

# ❌ 在父目录启动 → 读不到 .claude/rules/ → 规则丢失
terminal(command="cat task.md | claude -p ...", workdir="/opt/data/git-repos")

R.3 为什么双重

  • workdir 参数是声明,但某些封装/重定向可能不生效;
  • 命令内 cd执行保证,两者叠加才能确保万无一失。
  • 派发前先确认根目录:git rev-parse --show-toplevel 的结果应与 project_root 一致。

Q、版本规范与发布检查

Q.1 版本号小步迭代

0.0.1 → 0.0.12 → 0.0.99 → 0.1.1
  • 只增不减:新版本号必须大于当前已发布/已提交的版本,绝不回退。
  • 小步快跑:补丁 +0.0.1,新增能力 +0.1.0,破坏性变更 +1.0.0。
  • 三处对齐SKILL.md frontmatter、package.jsonlib/index.mjsVERSION)必须一致。

Q.2 发布前检查清单

引用 skill-core-guide §3.5 的 13 项发布前检查(逐项勾选,缺一不可):

  1. frontmatter 必填字段齐全(name/description/version/author/license/tags)
  2. description 写清"何时用"而非"是什么"
  3. 版本号三处对齐且单调递增
  4. package.json files 字段精确声明发布内容
  5. main/exports 指向 lib/index.mjs
  6. bin 可执行且有 shebang
  7. 无硬编码密钥/凭据
  8. README 含简介 / 快速开始 / 许可
  9. LICENSE 与 frontmatter 的 license 一致
  10. 商标声明完整(未来飞马 / Harness Loop / RSI)
  11. 六项自动质检全绿(skip 不算通过)
  12. 四渠道版本号一致
  13. commit message 不含内部工具名

Q.3 四渠道发布

Gogs → GitHub → npm → skillhub
  • 顺序固定:先内网 Gogs,再 GitHub,再 npm,最后 skillhub 上架。
  • 每渠道发布后验证:npm 用 npm view <pkg> version,git 用 git ls-remote
  • 任一渠道失败 → 停止后续渠道,先修复,避免版本分叉。

P、CDN 验证铁律

P.1 200 ≠ 真 200

对象存储/CDN 在资源不存在时,可能返回 404 首页且 HTTP 状态是 200

只看 %{http_code} 会得到假绿。必须叠加 Content-Type 判断。

P.2 分类型验证

资源 必须满足
图片 Content-Type: image/*
HTML Content-Type: text/html
其它(JS/CSS/JSON) Content-Type: application/*

P.3 逐 URL 验证,不可跳过

# ✅ 同时取 code 和 content_type
curl -m 10 -o /dev/null -w "%{http_code} %{content_type}\n" "$url"
# ✅ 断言式:不满足即报错
assert code == "200", f"HTTP {code}: {url}"
assert ct.startswith(("text/html", "image/", "application/")), f"类型异常 {ct}: {url}"

P.4 铁律汇总

  1. 200 不是通行证——必须看 Content-Type。
  2. 逐 URL 验证,不接受"抽查几个"。
  3. 断言而非目测——写成 assert,失败即中断。
  4. CDN 刷新前后各验一次(§T)——防旧缓存、防刷新后 404。
  5. 图裂是最隐蔽的坑——404 首页的 200 会让 HTML 看着正常,实际图全裂。

一句话:每个 URL 都要 curl + code + content_type 三重确认,一个都不能跳。