Agent Skills › monotykamary/pi-fabric › fabric-graph

fabric-graph

GitHub

构建基于 Fabric 的多步骤图工作流,支持会话内执行与可恢复的持久化 Actor 模式,用于处理需跨重启或人工决策的复杂流程。

skillsets/typescript/fabric-graph/SKILL.md fabric-runtime/pi-fabric

触发场景

需要构建多步骤自动化工作流 任务需跨越重启或等待人工审批

安装

npx skills add fabric-runtime/pi-fabric --skill fabric-graph -g -y
更多选项

非标准路径

npx skills add https://github.com/fabric-runtime/pi-fabric/tree/main/skillsets/typescript/fabric-graph -g -y

不安装直接使用

npx skills use fabric-runtime/pi-fabric@fabric-graph

指定 Agent (Claude Code)

npx skills add fabric-runtime/pi-fabric --skill fabric-graph -a claude-code -g -y

安装 repo 全部 skill

npx skills add fabric-runtime/pi-fabric --all -g -y

预览 repo 内 skill

npx skills add fabric-runtime/pi-fabric --list

SKILL.md

Frontmatter
{
    "name": "fabric-graph",
    "description": "Builds a step graph from plain Fabric programs. In-session graphs are ordinary code; resumable graphs are a durable actor that checkpoints each node in the mesh, schedules its next step, and parks human nodes on decisions. Use for multi-step flows that must survive restarts or wait for a person.",
    "disable-model-invocation": true
}

Fabric Graph

A graph is program code, not a framework. Pick the smallest form that fits:

  • In-session graph: one fabric_exec program. Nodes are calls; edges are if, loops, workflow.parallel, and budgets. Use it when the whole graph finishes in this turn.
  • Resumable graph: a durable actor that runs one node per mailbox message, checkpoints each node with mesh.put CAS, and wakes itself for the next node. Use it when a node waits on a person or a schedule, or the run must survive Main and resident-host restarts.

Nodes that are reviewed, reusable steps can be saved programs: programs.run({ ref, input, requirePromoted: true }) runs one nested with the caller's capabilities.

In-session graph

const target = π.target;
await workflow.configure({ name: `Graph · ${target}` });
await phase("Checks");
const [types, tests] = await workflow.parallel([
  () => pi.bash({ command: "bun run typecheck", settle: true }),
  () => pi.bash({ command: `bunx vitest run ${target}`, settle: true }),
]);
if (!types.ok || !tests.ok) {
  return { status: "failed", node: "checks", types: types.ok, tests: tests.ok };
}
await phase("Review");
const review = await agent<string>(`Review the change to ${target} for correctness. Reply APPROVE or a list of blockers.`);
return { status: review.startsWith("APPROVE") ? "success" : "partial", review };

Pass the test target as strings.target. A failed node returns its evidence; do not rerun passing nodes.

Resumable graph

Seed the run once, then create a durable actor that owns it. The state record is the single source of truth: runs/<run>/graph holds { node, status, results, decisionId? } with its mesh version.

const run = π.run;
const key = `runs/${run}/graph`;
await mesh.put({
  key,
  value: { node: "fetch", status: "ready", results: {} },
  ifVersion: 0,
});
const actor = await agents.create({
  name: `graph-${run}`,
  runner: "pi",
  residency: "durable",
  topics: [`graph.${run}`],
  instructions: [
    `You own the resumable graph ${key}. Nodes in order: fetch, test, approve, release.`,
    "On every message, run exactly one fabric_exec program that:",
    `1. reads ${key} with mesh.get and stops if status is "done" or "failed";`,
    "2. runs the current node only, writes its result and the next node with mesh.put({ ifVersion: <the version you read> }), and stops on a CAS conflict;",
    "3. for approve, raises decisions.raise({ title, input: \"confirm\" }) once, stores decisionId in the record, and waits with decisions.wait({ id, timeoutMs: 60000 }); while still open, schedules mesh.publish({ topic, kind: \"graph.tick\", afterMs: 600000, key }) and stops;",
    "4. after a successful node, wakes itself with agents.tell to its own id from mesh.self().",
    "Every node must be idempotent: before acting, check the recorded result; an interrupted spawn or shell run is indeterminate, so verify its effect before repeating it.",
  ].join("\n"),
});
await agents.tell({ id: actor.id, message: `Start graph ${key}.` });
return { run, key, actor: actor.id, topic: `graph.${run}` };

Pass the run key as strings.run. The actor's node program follows this shape:

const key = `runs/${π.run}/graph`;
type Graph = { node: string; status: string; results: Record<string, unknown>; decisionId?: string };
const entry = await mesh.get<Graph>({ key });
if (!entry || entry.value.status === "done" || entry.value.status === "failed") return { status: entry?.value.status ?? "missing" };
const graph = entry.value;
const advance = async (node: string, result: unknown, status = "ready") => {
  await mesh.put({ key, value: { ...graph, node, status, results: { ...graph.results, [graph.node]: result } }, ifVersion: entry.version });
  if (status === "ready") await agents.tell({ id: (await mesh.self()).id, message: `Continue ${key}.` });
};
if (graph.node === "approve") {
  let decisionId = graph.decisionId;
  if (!decisionId) {
    decisionId = (await decisions.raise({ title: `Release ${π.run}?`, input: "confirm" })).id;
    await mesh.put({ key, value: { ...graph, decisionId }, ifVersion: entry.version });
    return { status: "waiting", decisionId };
  }
  const decision = await decisions.wait({ id: decisionId, timeoutMs: 60_000 });
  if (decision.status === "open") {
    await mesh.publish({ topic: `graph.${π.run}`, kind: "graph.tick", afterMs: 600_000, key: `graph-${π.run}` });
    return { status: "waiting", decisionId };
  }
  const approved = decision.status === "answered" && decision.answer?.optionId === "yes";
  await advance(approved ? "release" : "approve", decision.status, approved ? "ready" : "failed");
  return { status: approved ? "approved" : "rejected" };
}
const result = await programs.run({ ref: `graph-${graph.node}`, input: { run: π.run }, requirePromoted: true });
await advance(graph.node === "fetch" ? "test" : graph.node === "test" ? "approve" : "done", result, graph.node === "release" ? "done" : "ready");
return { status: "advanced", from: graph.node };

Here each work node is a promoted saved program named graph-<node>; plain inline code works the same way.

Rules

  • One node per wake. The mesh record decides what runs next; mailbox text never does.
  • Every write is mesh.put with the version you read. A conflict means another wake advanced the graph: stop.
  • Human nodes park on decisions.raise/decisions.wait and recheck through a scheduled mesh.publish with a stable key, so a restart never raises a second decision.
  • Nodes are idempotent. Interrupted spawns are indeterminate after a restart: verify effects (files, commits, tickets) before repeating a node, and record the evidence in results.
  • Mesh events never run programs by themselves. An event may carry data.fabricProgram = { ref, input }; the subscribed actor decides to call programs.run.

Completion criterion

Complete when the in-session program returns success, partial, or failed with per-node evidence, or when the resumable graph is seeded, its durable actor is created and told to start, and you report the run key, state key, actor id, and topic.

版本历史

  • e55afc7 当前 2026-10-18 17:41

同 Skill 集合

skills/fabric-advisor/SKILL.md
skills/fabric-ambient/SKILL.md
skills/fabric-council/SKILL.md
skills/fabric-exec/SKILL.md
skills/fabric-fusion/SKILL.md
skills/fabric-guide/SKILL.md
skills/fabric-rlm/SKILL.md
skills/fabric-schema/SKILL.md
skills/fabric-spec/SKILL.md
skills/fabric-supervisor/SKILL.md
skills/fabric-swarm/SKILL.md
skills/fabric-workflow/SKILL.md
skillsets/python/fabric-advisor/SKILL.md
skillsets/python/fabric-ambient/SKILL.md
skillsets/python/fabric-council/SKILL.md
skillsets/python/fabric-exec/SKILL.md
skillsets/python/fabric-foreman/SKILL.md
skillsets/python/fabric-fusion/SKILL.md
skillsets/python/fabric-graph/SKILL.md
skillsets/python/fabric-guide/SKILL.md
skillsets/python/fabric-jev/SKILL.md
skillsets/python/fabric-rlm/SKILL.md
skillsets/python/fabric-schema/SKILL.md
skillsets/python/fabric-spec/SKILL.md
skillsets/python/fabric-supervisor/SKILL.md
skillsets/python/fabric-swarm/SKILL.md
skillsets/python/fabric-workflow/SKILL.md
skillsets/typescript/fabric-advisor/SKILL.md
skillsets/typescript/fabric-ambient/SKILL.md
skillsets/typescript/fabric-council/SKILL.md
skillsets/typescript/fabric-exec/SKILL.md
skillsets/typescript/fabric-foreman/SKILL.md
skillsets/typescript/fabric-fusion/SKILL.md
skillsets/typescript/fabric-guide/SKILL.md
skillsets/typescript/fabric-jev/SKILL.md
skillsets/typescript/fabric-rlm/SKILL.md
skillsets/typescript/fabric-schema/SKILL.md
skillsets/typescript/fabric-spec/SKILL.md
skillsets/typescript/fabric-supervisor/SKILL.md
skillsets/typescript/fabric-swarm/SKILL.md
skillsets/typescript/fabric-workflow/SKILL.md

元信息

文件数
0
版本
e55afc7
Hash
f642417a
收录时间
2026-10-18 17:41

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-10-20 00:09
浙ICP备14020137号-1