Archify Lifecycle Renderer 完全指南:用 JSON 构建可验证的阶段生命周期图
【免费下载链接】archifyAgent skill for beautiful, verifiable architecture, workflow, sequence,>项目地址: https://gitcode.com/GitHub_Trending/arch/archify
Archify 的 Lifecycle Renderer 是一种将diagram_type: "lifecycle"的 JSON 描述文件渲染成自包含 HTML 图表的专用渲染器,专为表达"阶段、事件与终态"并存的生命周期而设计——例如 Agent 任务运行的生命周期、部署发布流程、订单履约链路等。读完本文,你将掌握生命周期 JSON 的完整编写语法(lane/state/transition 语义)、布局预算与路由预设的使用方法,并能利用渲染器内置的零依赖校验与质量门禁(standard/showcase 双档)产出可验证、可交付的高质量 SVG/HTML 图表。
一、快速开始:渲染一个生命周期图
Lifecycle Renderer 的入口是 render-lifecycle.mjs,它是一个零依赖安装的 Node.js 命令行渲染器,用法如下:
node archify/renderers/lifecycle/render-lifecycle.mjs input.lifecycle.json output.html其输入为遵循 lifecycle.schema.json 的 JSON 文件,输出为套用标准 Archify HTML 模板的独立 HTML 文件。渲染器使用仓库内置的 standalone 校验器进行 Schema 校验,无需安装任何 npm 依赖。
output.html是可省略的:省略时渲染器会按以下优先级决定输出路径(对应 cli.mjs 中的resolveOutputPath逻辑):
- 命令行第二个参数(显式输出路径);
- JSON 中
meta.output字段; - 兜底为当前工作目录下的
lifecycle.html。
如果你想直接看到效果,仓库已内置一个完整可运行的示例,直接执行:
node archify/renderers/lifecycle/render-lifecycle.mjs archify/examples/agent-run.lifecycle.json即可按示例 JSON 中的meta.output(examples/lifecycle-agent-run.html)生成渲染结果。渲染器入口的默认示例正是agent-run.lifecycle.json(见 render-lifecycle.mjs)。
二、输入结构:Lifecycle JSON 的顶层骨架
一份合法的 Lifecycle JSON 必须包含以下顶层字段,最小骨架如下(摘自 README):
{ "schema_version": 1, "diagram_type": "lifecycle", "meta": { "title": "Agent Run Lifecycle", "viewBox": [980, 660] }, "lanes": [], "states": [], "transitions": [], "cards": [] }依据 lifecycle.schema.json,各字段的约束为:
| 字段 | 必填 | 约束 |
|---|---|---|
schema_version | 是 | 固定为1(const) |
diagram_type | 是 | 固定为"lifecycle"(const) |
meta | 是 | 至少包含非空title |
lanes | 是 | 数组,长度 1~4,每项必填id与label |
states | 是 | 数组,至少 2 项 |
transitions | 是 | 数组,每项至少包含from与to |
cards | 否 | 摘要卡片数组(见下文) |
整个 Schema 采用additionalProperties: false严格模式,任何未声明字段都会被直接判为非法。
2.1 语义化且保留的 Lane ID
生命周期图的三个横向"带"(band)完全由 lane 的 id 决定,这是整个布局体系的核心约定(源码实现在 render-lifecycle.mjs 的bandFor()):
main(必填):映射到顶部"阶段带"(Phase band),承载主生命周期横轨;terminal:映射到底部"结果带"(Outcome band),承载终态退出;- 其他任意 id:最多 4 条 lane 总数内,所有非
main/terminal的 lane共享同一个中部"事件带"(Event band),彼此之间通过yOffset在视觉上错开。
三条带的标题文字直接取自你的 lane label(见bandTitles()与renderBands()):中部事件带会把所有事件 lane 的 label 用+拼接,例如"Interruptions + Recovery loop"。
2.2 完整实战示例:Agent Run Lifecycle
仓库中的 agent-run.lifecycle.json 是文档明确指出的完整工作示例,它演示了主阶段带 + 事件带 + 终态带的全部语义:
{ "schema_version": 1, "diagram_type": "lifecycle", "meta": { "title": "Agent Run Lifecycle", "output": "examples/lifecycle-agent-run.html", "viewBox": [980, 660], "animation": "trace", "quality_profile": "showcase", "views": [ { "id": "main-lifecycle", "label": "Main lifecycle", "focus": ["queued", "planning", "executing", "reviewing", "completed"], "note": "Follow the ordered phases from accepted request to completed response." }, { "id": "human-waits", "label": "Human and input waits", "focus": ["executing", "approval", "reviewing", "blocked"], "note": "See where the run pauses without becoming terminal." }, { "id": "recovery-and-exits", "label": "Recovery and terminal exits", "focus": ["executing", "failed", "blocked", "cancelled", "expired"], "note": "Separate retryable failure from cancellation and expiry." } ] }, "lanes": [ { "id": "main", "label": "Lifecycle phases" }, { "id": "waiting", "label": "Interruptions" }, { "id": "exceptions", "label": "Recovery loop" }, { "id": "terminal", "label": "Terminal exits" } ], "states": [ { "id": "queued", "type": "start", "label": "Queued", "sublabel": "request accepted", "lane": "main", "col": 0, "step": "01", "tag": "entry" }, { "id": "planning", "type": "active", "label": "Planning", "sublabel": "build task graph", "lane": "main", "col": 1, "step": "02", "tag": "model" }, { "id": "executing", "type": "active", "label": "Executing", "sublabel": "tool calls", "lane": "main", "col": 2, "step": "03", "tag": "work" }, { "id": "reviewing", "type": "decision", "label": "Reviewing", "sublabel": "quality gate", "lane": "main", "col": 3, "step": "04", "tag": "check" }, { "id": "completed", "type": "success", "label": "Completed", "sublabel": "final response", "lane": "main", "col": 4, "step": "05", "tag": "done" }, { "id": "approval", "type": "waiting", "label": "Needs Approval", "sublabel": "human gate", "lane": "waiting", "col": 0, "tag": "pause" }, { "id": "blocked", "type": "waiting", "label": "Blocked", "sublabel": "missing input", "lane": "waiting", "col": 1, "tag": "wait" }, { "id": "failed", "type": "failure", "label": "Failed", "sublabel": "recoverable error", "lane": "exceptions", "col": 0, "yOffset": 78, "tag": "retryable" }, { "id": "cancelled", "type": "failure", "label": "Cancelled", "sublabel": "user stopped", "lane": "terminal", "col": 0, "tag": "terminal" }, { "id": "expired", "type": "failure", "label": "Expired", "sublabel": "timeout", "lane": "terminal", "col": 1, "tag": "terminal" } ], "transitions": [ { "id": "approval-needed", "from": "executing", "to": "approval", "variant": "security", "fromSide": "bottom", "toSide": "top", "route": "straight" }, { "id": "review-blocked", "from": "reviewing", "to": "blocked", "variant": "default", "route": "drop" }, { "id": "execution-failed", "from": "executing", "to": "failed", "variant": "security", "fromSide": "left", "toSide": "left", "via": [[320, 157], [320, 385]] }, { "id": "failed-retry", "from": "failed", "to": "executing", "variant": "emphasis", "fromSide": "left", "toSide": "top", "via": [[20, 385], [20, 80], [402, 80]] }, { "id": "block-expired", "from": "blocked", "to": "expired", "variant": "security", "fromSide": "bottom", "toSide": "top", "route": "straight" }, { "id": "approval-cancelled", "from": "approval", "to": "cancelled", "variant": "security", "fromSide": "bottom", "toSide": "top", "via": [[480, 336], [480, 432], [402, 432]] } ], "cards": [ { "dot": "emerald", "title": "Main Path", "items": ["The run has five ordered phases from queue to completion", "The primary lifecycle is carried by one horizontal rail", "Completion is a phase, not a detached side box"] }, { "dot": "amber", "title": "Human + Input Gates", "items": ["Approval pauses execution without ending the run", "Blocked waits for missing user input", "Wait states remain non-terminal until cancellation or expiry"] }, { "dot": "rose", "title": "Terminal + Recovery", "items": ["Failed loops back while retry budget remains", "Cancelled and Expired are exits from the lifecycle", "Terminal exits do not point back into active execution"] } ] }这个示例同时体现了meta.views(引导视图,最多 5 个,每个含focus语义 id 列表)、animation: "trace"(轨迹动画)、quality_profile: "showcase"与cards(摘要卡片)等高级特性。仓库还在 test/fixtures/v1-baseline/agent-run.lifecycle.json 提供了一份不依赖任何高级特性的 v1 基线版本,用于兼容性回归验证。
三、State 与 Transition 字段详解
3.1 State 字段
依据 lifecycle.schema.json,每个 state 必填id、type、label、lane、col,可选字段如下:
| 字段 | 类型/约束 | 说明 |
|---|---|---|
type | 枚举:start/active/waiting/decision/success/failure/neutral/external | 决定配色、图例项与语义标记(见typeClass/textClass映射,render-lifecycle.mjs) |
sublabel | 字符串 | 主标签下方的次要说明,随详情层(detail)展示 |
tag | 字符串 | 状态右下角的角标文本,如"entry"、"pause" |
step | 字符串 | 有序阶段序号,如"01"、"02",渲染在状态左上角 |
brand | 字符串或{url, sha256}对象 | 品牌标记(brand mark),见共享品牌协议 |
col | 整数 0~4 | 列号,对应所在带的列中心表(见下文布局预算) |
width/height | 数字,分别 ≥48 / ≥36 | 覆盖该带默认状态尺寸 |
yOffset | 数字 | 纵向偏移,用于在同一事件带内错开不同 lane 的状态 |
3.2 Transition 字段
每个 transition 必填from、to(均引用 state id),可选字段:
| 字段 | 说明 |
|---|---|
id | 可选但推荐;一旦提供即成为查看器链接的持久身份,且全局唯一(见 cli.mjs 的validateRelationshipIds) |
label/note | 连线标签与附注 |
variant | 枚举default/emphasis/security/dashed,决定线型与箭头样式 |
route | 路由预设:auto(默认)、straight、drop、bottom-channel、top-channel、right-channel、left-channel |
fromSide/toSide | 端点出口/入口方向:left/right/top/bottom |
channelX/channelY | 手动指定通道坐标,覆盖路由预设的默认通道位置 |
cornerRadius | 多段连线圆角半径,默认 10,0为直角折线 |
via | 显式途经点数组(每点为[x, y]),作者指定的点具有最高优先级 |
width | 线宽(≥0.5) |
labelAt/labelDx/labelDy/labelSegment | 标签位置微调:绝对点位、dx/dy 偏移、所在线段索引 |
3.3 meta 共享字段
meta还支持来自 common.schema.json 的共享定义:
locale:en或zh-CN(渲染器内部文本与无障碍文案据此本地化,见 i18n.mjs);animation:trace(轨迹动画)或none;visual_preset:classic/signal-flow/blueprint/editorial;quality_profile:standard(默认)或showcase;views:引导视图,最多 5 个;legend:图例覆盖配置;viewBox:[宽度, 高度]数组,宽度 ≥420、高度 ≥566(Schema 最小值),默认[980, 660]。
四、布局预算:三带坐标系
Lifecycle 渲染器采用固定化的三带布局,坐标在 render-lifecycle.mjs 中定义为常量。README 给出了完整的布局预算表:
| 带 | Lane id | Top y | 列中心 | 默认状态尺寸 |
|---|---|---|---|---|
| 阶段带 Phase | main(必填) | 126 | col0–4 → x = 94, 248, 402, 556, 710 | 118×62 |
| 事件带 Event | 其他任意 id | 278 | col0–2 → x = 402, 556, 710 | 126×58 |
| 结果带 Outcome | terminal | 450 | col0–2 → x = 402, 556, 710 | 118×58 |
关键设计:事件带与结果带的列是相对主阶段轨有意偏移的——下带col: N与主带col: N + 2使用相同的 x 坐标。也就是说,下带第 0、1、2 列分别对齐在主带第 2、3、4 列的正下方。这种对齐让"中断/终态"天然落在主流程后半段的下方,视觉上形成稳定的竖直"出口通道"。
4.1 布局常量汇总
| 常量 | 值 |
|---|---|
| viewBox | 默认[980, 660];Schema 最小[420, 566] |
| 状态水平边界 | x 落在[32, width − 32]内;状态底部不得低于height − 122 |
| 状态间距 | 任意两个状态之间 ≥10px——跨 lane 检查,因为所有事件 lane 共享同一条带;同带内靠col或yOffset分开 |
| 连线长度 | 端点间距 ≥32px |
| 图例行基线 | 最终基线 y =height − 36;额外测量的图例行向上回绕 |
主生命周期轨(primary lifecycle rail)沿阶段带运行,并延伸到最右侧被占用的阶段列(见renderLifecycleRail(),轨线从x=154起以 2.2px 强调线 + 箭头收尾)。
4.2 路由预设
过渡连线的路径完全由route预设 + 通道坐标 +via途经点决定,源码实现在routeVia()(render-lifecycle.mjs):
| 预设 | 行为 |
|---|---|
straight | 直线,无中间点 |
drop | 在channelY处折弯(默认取起终点纵向中点) |
bottom-channel | 在channelY处水平折弯(默认max(两状态底边) + 34) |
top-channel | 在channelY处水平折弯(默认min(两状态顶边) − 28) |
right-channel | 在channelX处竖直折弯(默认max(两状态右边) + 36) |
left-channel | 在channelX处竖直折弯(默认min(两状态左边) − 36) |
via | 显式途经点,完全由作者指定,最高优先级 |
auto(默认) | 按端点出/入方向自动推断单弯或双弯 |
多段连线会统一做圆角处理,cornerRadius默认 10,设为0得到锐利直角折线。自动路由的端点还会经过automaticPortSpread(自动端口分散)处理,同侧多条连线会自动在状态边上分散锚点,避免端口重叠(见 render-lifecycle.mjs)。
五、图例(Legend)
生命周期图例默认从states[].type推导需要展示的种类。meta.legend.entries支持按稳定顺序覆盖以下 key:start、active、waiting、decision、success、failure、neutral、external(该稳定顺序同时被渲染器的LEGEND_CATALOG固化,见 render-lifecycle.mjs)。
共享图例契约(legend.mjs)支持三种模式与逐项覆盖:
"legend": { "mode": "auto", "entries": { "success": { "label": "完成", "visible": true }, "failure": { "label": "失败/终态", "visible": true }, "neutral": { "visible": false } } }mode:auto(只展示图中出现的种类)、all(展示全部)、hidden(不渲染图例);- 每项可覆盖
label与visible; - 只有被图中实际渲染状态支撑的种类才会获得 Semantic Legend 交互控件(
interactive标记取决于该 kind 是否 present,见resolveLegend())。
图例的布局由共享measureLegend()统一计算:自动换行、多行向上回绕、行数超限时在unfit: 'error'(作者显式声明图例时)与unfit: 'hide'(未声明时)之间选择失败或隐藏行为。
六、设计规则:把生命周期画成"阶段地图"
README 明确给出了一套创作纪律,这是 Lifecycle 类型区别于普通状态机图的核心哲学:
- 把生命周期图当作阶段地图(phase map),而不是密集的状态转移图——不要试图塞进所有状态细节;
- 用
mainlane 把主生命周期放在一条水平轨上; - 用
step标签标记有序阶段,如01、02、03; - 下带只用于中断、恢复与终态退出;
- 除非必要,不要把连线标签放进主 SVG——优先使用节点标签、tag、图例项与摘要卡片传达信息;
- 避免斜线与交叉线;终态退出应尽可能从源事件竖直下坠;
- 语义选型约定:
success表示完成、failure表示失败/终态退出、waiting表示暂停、decision表示质量门禁。
七、校验与质量门禁:渲染失败即交付失败
Lifecycle Renderer 的校验分两层执行,全部在写文件之前完成(validateLifecycle()在 render-lifecycle.mjs 中定义,出错时通过throwDiagnosticProblems以非零退出码结束)。
7.1 Schema 层
违反 Schema 的输入会以带路径前缀(path-prefixed)的错误消息退出,消息会标注到具体元素的 id 或 label,方便快速定位。
7.2 布局层(可检测即失败)
渲染器在 Schema 之外还做了大量几何级检测,任一失败都会阻止输出:
- 缺少
mainlane、重复的 state id、未知 lane、未知连线端点; - 状态超出生命周期区域(水平越界或越过
height − 122下边界); - 状态重叠(含跨 lane)、标签与状态或其他标签碰撞、标签宽于所属状态、过短到不可读的连线;
- 连线穿越无关状态(2px Clean Flow 净空);
- 生命周期带是有意的直通容器(pass-through containers),不作为障碍物参与路由;
- 文本宽度采用CJK 感知估算:全角字形按 2 个单位计宽(
textUnits),中文标签不会出现宽度误判。
7.3 quality_profile:standard 与 showcase 双档
设置meta.quality_profile: "showcase"可获得更严苛的交付级校验:
| 检测项 | standard(默认) | showcase |
|---|---|---|
| 无关连线 proper X 交叉 | 作为 artifact-receipt 警告 | 失败,报composition/proper-crossing |
| 无关连线重叠 ≥8px(共线走廊) | 警告 | 失败 |
| 任意路由段 <8px | 容忍 | 失败 |
| 内部拐弯段 <16px | 容忍 | 失败 |
| 8–15px 的普通端点短桩 | 有效 | 仍有效 |
最后的产品检查还会抽样验证圆角Q命令是否存在(保证圆角真正落到 SVG 输出中)。另外,作者显式指定的via途经点在 v1 Schema 中是权威性的:即使处于 showcase 档,也会被原样渲染而不做端点侧门禁的二次篡改,只有自动路由才应用端点侧门禁(见 render-lifecycle.mjs 的注释与shouldCheckRelation)。
八、渲染流水线与产物
渲染器的执行流程(renderSvg(),render-lifecycle.mjs)依次组装:
- 背景网格(
url(#grid)填充); - 三条生命周期带:
renderBands()绘制 112/264/436 处的虚线段(stroke-dasharray="3,8")与带标题,标题带01 /、02 /、03 /前缀; - 主生命周期轨:
renderLifecycleRail(); - 连线路径:
renderTransitionPath()输出带data-composition-points(供后续几何审计)的 path,并按variant选择箭头 marker; - 状态节点:
renderState()输出带data-node-*语义属性、可聚焦(tabindex="0")的节点组,按animateAttr附加--step动画步进; - 连线标签:
renderTransitionLabel(); - 图例:
renderLegend()。
最后writeDiagram()(cli.mjs)把 SVG 与摘要卡片填充进 assets/template.html,写入独立 HTML 文件——该产物自带查看器交互(节点聚焦、引导视图、语义图例、动画),可离线打开。此外,产物还会附带仓库证据(repository evidence)数据(verifyRepositoryEvidence),让图表可被追溯。
九、进阶:动画、引导视图与摘要卡片
9.1 动画
meta.animation: "trace"会为节点与连线附加data-animate与--step步进样式,实现按拓扑顺序的轨迹动画;步进数上限 12(见animateAttr),以保证在固定时长的 WebM 捕获窗口内完成。
9.2 引导视图(Guided Views)
meta.views数组(最多 5 项)把复杂生命周期拆成可讲解的焦点视图,每项含id、label、focus(语义 id 列表)与可选note。共享校验validateGuidedViews会强制focus引用的 id 必须真实存在且无重复,避免出现"视图指向不存在的状态"。
9.3 摘要卡片(Cards)
cards数组把核心结论沉淀为图下方的摘要卡(点色可选cyan/emerald/violet/amber/rose/orange/slate),适合用三张卡片分别总结"主路径 / 人工与输入门禁 / 终态与恢复"。
十、把 Lifecycle 渲染器接入你的流程
Lifecycle Renderer 可以被任何语言/脚本以子进程方式调用:传入 JSON 路径,读取 stdout 的输出路径,检查退出码即可判断校验是否通过。批量渲染场景可以参考 render-examples.mjs 中['lifecycle', 'agent-run.lifecycle.json', 'lifecycle-agent-run.html']的注册方式,把 lifecycle 渲染纳入统一渲染管线。若希望以当前仓库为证据根目录做仓库证据注入,可通过ARCHIFY_REPO_ROOT环境变量指定。
适用场景总结:Agent 任务运行生命周期、CI/CD 流水线阶段与恢复、部署发布流程、人工审批门禁、任何"有序阶段 + 中断/恢复 + 终态"并存的过程建模。记住一句话:主流程上横轨,中断与终态下坠,质量门禁用decision,交付前记得打开showcase。
【免费下载链接】archifyAgent skill for beautiful, verifiable architecture, workflow, sequence,>项目地址: https://gitcode.com/GitHub_Trending/arch/archify
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考