双向依赖对账Archify 的静态扫描到底在查什么【免费下载链接】archifyTurn any idea, plan, or codebase into a beautiful interactive diagram. An agent skill for Claude Code, Codex, and more.项目地址: https://gitcode.com/GitHub_Trending/arch/archify如果让大模型直接输出一张架构图你会得到一张「看起来对」的图而 Archify 的做法是让大模型只输出一份结构化 JSON再由确定性程序完成渲染、校验和修复。最近几周 GitHub 热榜上围绕 Archify 的讨论反复出现「可核验」「自动对账」「五道校验」这些词但很少有人说清楚这份静态扫描到底在查什么为什么一张关系图需要「双向」比对以及那些规则为什么长成声明式 JSON 而不是一堆散落的 if 判断。这篇文章直接进源码从校验器、Delta 对账器和 workflow 语义契约三条线拆开 Archify 的静态扫描内核。从代码到依赖图校验发生在渲染之前Archify 的产物链是「AI 生成结构化 JSON → 确定性程序渲染」。这一步的关键在于JSON 只是一堆约定俗成的数据真正定义「什么样的 JSON 合法」的是 schemas/architecture.schema.json 这类 JSON Schema——组件必须有id、type、label连接必须声明from与to且from/to必须复用节点 id 的正则约束^[a-zA-Z][a-zA-Z0-9_-]*$。Schema 本身不是代码运行时靠的是由 scripts/generate-validators.mjs 生成的 renderers/shared/generated-validators.mjs——一份把五类图workflow、sequence、dataflow、lifecycle、architecture的 schema 全部编译为可执行校验函数的内嵌文件。它开头就写着「Generated by scripts/generate-validators.mjs. Do not edit by hand.」保证校验逻辑与 schema 永远同步。入口在 renderers/shared/validator.mjs 的validateSchema按图类型取出对应校验器失败时不会只丢一句「校验失败」而是把每个错误映射成带code、subject、evidence、supportedFixes的结构化诊断。比如additionalProperties会提示「remove unsupported property …」required会提示「add required property …」。为了让大模型能修annotatedPath还会把/nodes/3/label这种路径解析成/nodes/3 (id: router) /label——报错信息里带上最近元素的 id 或 label这是修复闭环能成立的前提。更关键的是「证据」这一层。节点上可以挂sources字段common.schema.json 中定义为sourceReferences约束path必填、line/end_line可选的数组最多 3 条把每个组件钉到仓库里的具体文件与行号meta.repository则要求同时给出url与 40 位十六进制的revision。也就是说图上每个框都声明了「我在代码里的证据在哪」这为后面 Delta 对账里的evidence变更分类埋下了伏笔。双向比对为什么单向检查拦不住漂移很多人以为「校验」就是检查图里有没有未知节点但 Archify 真正做的是双向对账——对每一条关系同时检查它的两个端点并且对每个节点的入度、出度同时做约束。这有两层含义。第一层在渲染期的端点检查。以 renderers/architecture/render-architecture.mjs 为例对每一条 connection 它同时检查两端if (!components.has(conn.from)) problems.push(Connection ${conn.label || conn.from} references unknown source ${conn.from}.); if (!components.has(conn.to)) problems.push(Connection ${conn.label || conn.to} references unknown target ${conn.to}.);这不是 architecture 图独有的特例——sequence 的消息检查from/to参与方render-sequence.mjs、lifecycle 的转移检查from/to状态render-lifecycle.mjs、dataflow 的流检查from/to节点render-dataflow.mjs全部是双端成对出现。对应测试也写进了 layout-rules.test.mjs把connections[0].to改成ghost断言输出unknown target ghost。这类悬空边正是「单向检查」最容易漏掉的漂移形态——只校验「源节点存在」而不管目标图上就会画出一根指向空气的箭头。第二层在版本对账器 delta/architecture-delta.mjs。它把同一架构的 Before / After 两份快照做规范化后逐一比对canonicalArchitecture会对组件按 id 排序、对sources数组做内容级排序、对 connections 按 id 建立稳定索引——先保证「同一张图」无论书写顺序如何都产生相同的规范形再开始 diff。比对结果按字段分组分类const COMPONENT_FIELDS { semantic: [type, label, sublabel, tag, brand, icon], evidence: [sources], geometry: [row, col, pos, size], }; const CONNECTION_FIELDS { topology: [from, to], semantic: [label, variant], geometry: [fromSide, toSide, route, via, ...], };这个分组本身就是一份「对账语义字典」改from/to是拓扑变更改 label 是语义变更改sources是证据变更改坐标是几何变更。compareEntities对同一 id 在两边做对称扫描——只在 base、只在 head、两边都在但字段不同——生成 added / removed / changed 三类变更并产出带 Before / Delta / After 三态视图和机器可读 receipt 的审查产物。没有稳定 id 或出现重复 id 时直接以delta/stable-id-required、delta/duplicate-stable-id失败而不是静默猜测哪条边对应哪条边。workflow 的语义契约把「双向」推到了图论层面。renderers/workflow/workflow-compiler.mjs 的semanticContractDiagnostics先为每个节点统计incoming与outgoing两套度数allowedRoots出现时它是「零入度节点」的完整白名单任何没有入边又不在名单里的节点都会报workflow/unexpected-rootallowedTerminals对称地约束「零出度节点」报workflow/unexpected-terminalrequiredEdges要求某条有向边精确存在按from → to查集合requiredPaths则通过一个 BFS 的可达性函数reachable(from, to)验证从 A 到 B 存在一条有向路径。注意这里的措辞requiredEdges要求「一个精确的书写方向」requiredPaths允许中间节点但必须顺着边的方向。也就是说即使 A 到 B 在无向意义上是连通的只要方向不对照样判定失败——这正是「单向检查」永远给不出的保证。校验规则的可声明性与误报调优把校验规则写成声明式 JSON 而不是埋在代码里收益在 workflow.schema.json 里看得很清楚semanticChecks: { type: object, additionalProperties: false, minProperties: 1, properties: { allowedRoots: { type: array, items: { $ref: #/$defs/id } }, allowedTerminals: { type: array, items: { $ref: #/$defs/id } }, requiredEdges: { type: array, items: { $ref: #/$defs/semanticRelation } }, requiredPaths: { type: array, items: { $ref: #/$defs/semanticRelation } } } }规则集合本身就是 schema 的一部分多一个规则名就是多一个additionalProperties白名单之外的键——这直接让「规则」可以被静态校验、被工具链审阅、被测试覆盖。编译器的 READMErenderers/workflow/README.md给出了明确的使用原则allowedRoots/allowedTerminals一旦出现就是完整名单且「这些检查在布局之前运行、不改写 SVG 或 receipt 字节、不得仅为解决一个路由诊断而弱化规则领域事实未知就省略对应字段」。误报调优靠的是「声明而非关闭」当编译器报告一个语义违规时它给出的两条supportedFixes都指向补充事实而不是放宽检查。以workflow/unexpected-root为例修复建议是「给该节点补上缺失的入边」或「如果它本是有意作为源则把它声明进allowedRoots」——前者的本质是把漂移修掉后者的本质是把「它确实是源」这个领域事实显式写进契约。两者都让规则更完整而不是让规则失效。这一点和整个工具的交付哲学一致。SKILL.md 规定finalize命令的第一道门就是 showcase 校验且「非零退出码永远不是成功」失败时按 receipt 中的稳定规则码、subject、measured evidence 与supportedFixes修复而不是对着 Node 堆栈盲猜。校验从「一次通过/不通过」变成了「带可执行修复建议的闭环」——这正是社区讨论里反复出现的「Archify 交图前过五道校验」的源码落点。小结把 Archify 的静态扫描拆开看它在查的事情其实非常具体schema 是否合法、每条边两个端点是否都存在、每个零入度/零出度节点是否被显式声明、sources证据是否随版本变化、以及requiredPaths的可达性是否在正确的方向上成立。双向对账的价值不在于「查得更多」而在于让漂移无处遁形——悬空边、反向路径、未声明的根与终端这些恰好都是单向检查的结构性盲区。当规则本身变成可声明的 JSON、错误变成带证据与修复建议的结构化诊断静态扫描才真正从「门禁」变成了「对账」。【免费下载链接】archifyTurn any idea, plan, or codebase into a beautiful interactive diagram. An agent skill for Claude Code, Codex, and more.项目地址: https://gitcode.com/GitHub_Trending/arch/archify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考