关于 architecture-scaffold
architecture-scaffold 是将高层架构规范转为可编译类型骨架的 Agent 技能,在加入实现逻辑前验证结构。它涵盖模块映射、分层编译、依赖检查,以及将现有代码映射到新结构。
主要功能
- 01提取模块、类型与签名
- 02定义分层依赖规则
- 03逐层构建类型桩
- 04编译并验证依赖方向
- 05将现有逻辑映射到骨架
适用场景
- 01依据架构规范搭建重构骨架
- 02通过编译反馈检查模块边界
- 03规划现有逻辑迁移
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ petekp/ claude-code-setup --skill architecture-scaffold安装后在 Claude Code 中输入/architecture-scaffold确认已加载。
附带文件references/language-patterns.md
详细介绍
架构脚手架
将高层架构规范转化为可编译的类型骨架,并在任何人编写一行逻辑之前,通过编译器验证其正确性。
为何需要它
大型重构常因 Agent 直接着手实现而失败。它们会偏离方向,做出与整体设计相矛盾的局部决策,最终留下的烂摊子与最初的问题截然不同。“人类构建外壳”范式(Mengdi Chen,2026)通过分离结构与逻辑来解决这个问题:
- 类型是硬性约束——编译器会在构建时拒绝违规内容
- 测试是行为验证——确认代码在运行时的行为
- 规范是软性指导——提供参考,但无法强制执行
此技能作用于第 1 层。完成后,每个模块、每个函数签名、每个协议/trait,以及每种类型关系都将以真实代码的形式存在,并经过编译器验证。此时还没有逻辑,只有架构骨架。编译器一旦报错,Agent 就无法凭空编造来蒙混过关。
三个阶段
阶段 1:提取目标架构
阅读规范。整理出每个模块、类型和签名的结构化大纲。
阶段 2:构建骨架
编写包含真实类型和存根函数体的源文件。逐层编译,直至全部通过。
阶段 3:映射旧代码库
针对每个存根,判断现有逻辑是否可以移植,或是否需要重写。
阶段 1:提取目标架构
阅读用户提供的评估或设计文档。需要关注:
- 模块/组件——新的架构单元有哪些?
- 职责——每个单元负责什么?
- 类型——哪些数据结构会跨越边界?
- 依赖关系——哪些模块依赖哪些模块?依赖方向是什么?
- 边界——硬性分界位于何处?(FFI、网络、持久化、框架边界)
生成一份模块映射——能够涵盖上述内容的结构化大纲。暂时不要编写代码。模块映射是介于文本规范和代码骨架之间的中间表示。
# Module Map
## Layer 1: Domain Types (innermost — no dependencies on other project modules)
### module: domain
Location: core/capacitor-core/src/domain/
Responsibility: Shared value types, identities, and enums used across all modules
Types:
- Project { id, name, path, ... }
- RuntimeSnapshot { active_project, hooks, timestamp, ... }
- HookEvent (enum: session_start, session_end, tool_use, ...)
Dependency rule: This module imports nothing from the project. Everything else may import this.
## Layer 2: Service Contracts (depend only on domain types)
### module: RuntimeEngine
Location: core/capacitor-core/src/runtime/
Responsibility: Core runtime lifecycle — start, stop, snapshot reads
Dependencies: domain (inward only)
Exposes:
- RuntimeEngine (protocol/trait)
- start(config: RuntimeConfig) -> Result<(), RuntimeError>
- stop() -> Result<(), RuntimeError>
- current_snapshot() -> RuntimeSnapshot
Types:
- RuntimeConfig { storage_path, poll_interval, ... }
- RuntimeError (enum: already_running, storage_unavailable, ...)
### module: SetupService
Location: core/capacitor-core/src/setup/
Responsibility: First-run and configuration workflows
Dependencies: domain (inward only)
Exposes:
- SetupService (protocol/trait)
- validate_setup() -> SetupStatus
- perform_setup(config: SetupConfig) -> Result<(), SetupError>
...
## Layer 3: FFI Boundary (translates between layers)
### module: ffi
Location: core/capacitor-core/src/ffi/
Responsibility: Expose Rust services to Swift via C-compatible interface
Dependencies: Layer 2 services, domain types
Boundary types (must be repr(C) or serializable):
- FFIRuntimeConfig, FFIRuntimeSnapshot, ...
Binding mechanism: [cbindgen / uniffi / manual C headers — match what the project uses]
## Layer 4: Swift Application Layer (outermost — depends on FFI)
### module: RuntimeSupervisor
Location: apps/swift/Sources/Capacitor/Services/RuntimeSupervisor.swift
...
依赖规则
模块映射必须明确声明每一层的依赖规则。这些规则将在验证过程中转化为可强制执行的约束:
Layer 1 (domain) → imports nothing from the project
Layer 2 (services) → imports only Layer 1
Layer 3 (FFI) → imports Layers 1 and 2
Layer 4 (Swift app) → imports only Layer 3's public interface
这些规则可以防止架构再次退化为一团乱麻。请将它们写下来。它们将在阶段 2 中通过机械方式验证。
细节程度很重要
每个函数签名都应包含参数名称、参数类型和返回类型。如果评估没有说明这些内容,请根据所述职责和现有代码库推断。标出任何不确定之处——在继续编写代码之前,应由用户确认。
处理模糊之处
评估中不可避免地会存在缺漏。常见情况包括:
- 错误类型——规范说“提取 SetupService”,却没有说明它可能产生哪些错误。查看现有代码,了解当前实现会处理哪些错误,并据此设计错误枚举。
- 共享类型——两个模块都需要访问
Project。该类型应该放在哪里?放在两者都依赖的领域层中(依赖指向内层)。 - 边界数据——哪些内容会跨越 FFI 或持久化边界?这些类型需要可序列化。请明确标出。
如有疑问,请询问用户。现在花五秒钟澄清,可以避免之后返工一小时。
利用评估中已有的参考信息
如果评估列出了具体文件、函数和行号(好的评估会这样做),请将它们用作推断签名的依据。当评估已经指出 lib.rs:276 中的 CoreRuntime.initialize() 时,不要从头搜索代码库。查看现有内容,并据此设计新的签名。
展示模块映射并征求确认
开始编写代码前,先向用户展示模块映射。请用户确认:
- 这些模块涵盖了评估中的所有建议
- 依赖方向正确
- 文件和目录的放置方式适合该项目
- 没有遗漏任何关键内容
- 粒度合适(既没有过多细小模块,也没有过少、过于臃肿的模块)
阶段 2:搭建骨架
现在开始编写实际代码。编写每个模块、每种类型和每个函数签名,但不包含实现逻辑。函数体使用桩实现。
创建工作分支
git checkout -b architecture-scaffold
文件放置
新文件的放置位置很重要——它决定了模块图。请遵循以下原则:
- 新模块应放入新文件或目录中,不要插入现有文件。如果要将
CoreRuntime拆分为RuntimeEngine和SetupService,请创建src/runtime/mod.rs和src/setup/mod.rs,暂时不要尝试从lib.rs中拆分代码。 - **模块映射中的“Location”字段是唯一依据。**你已在阶段 1 确定了文件放置方式;现在按计划执行。
- **暂时不要动旧文件。**骨架与现有代码并存。不要删除或修改旧代码——这是迁移阶段的工作。
- 更新模块声明(Rust
mod语句、Swift 软件包目标),让编译器能够识别新文件。
逐层搭建
不要一次性写完所有骨架文件后再编译。应从内向外逐层构建,并在每一层进行编译。这样可以将错误限制在局部,避免问题层层蔓延。
步骤 1:领域类型(第 1 层) 编写共享类型——结构体、枚举、类型别名。然后编译。 这些类型没有依赖,因此如果无法编译,问题就只在本层。
步骤 2:服务契约(第 2 层) 编写协议/trait 及其关联类型。编写桩实现,然后编译。 如果编译失败,原因要么是导入错误(容易修复),要么是与领域层类型不匹配(架构问题——见下文)。
步骤 3:FFI 边界(第 3 层) 编写 FFI 类型,以及 Rust 服务与 Swift 之间的转换层。本层必须使用项目已有的绑定机制(cbindgen、uniffi、手动编写的 C 头文件等)。分别编译 Rust 和 Swift 两端,并验证生成的绑定匹配。
步骤 4:Swift 应用层(第 4 层) 编写 Swift 协议、桩实现,以及面向 SwiftUI 的类型,然后编译。
特定语言模式
Swift、Rust 和 TypeScript 的完整示例请参见 references/language-patterns.md。所有语言遵循的核心模式都相同:定义契约(protocol/trait/interface),定义包含所有字段的所有类型,并提供能够通过编译、但被调用时会触发 panic 的桩实现(Swift 中使用 fatalError,Rust 中使用 todo!(),TypeScript 中使用 throw new Error)。
骨架的内容
包含:
- 所有包含完整方法签名的协议/trait
- 包含所有字段/变体的所有结构体、枚举和类型别名
- 每个协议/trait 的桩实现(以便编译器验证是否符合要求)
- 模块声明和导入/导出结构
- 每个模块的公共 API
- FFI 边界类型和绑定层(头文件、uniffi 定义等)
不包含:
- 任何实际业务逻辑
- 测试文件(这些将在之后的迁移阶段添加)
- 配置文件、构建脚本和 CI——除非新架构要求对这些内容进行结构性调整
响应编译器错误
并非所有编译器错误都一样。要区分两类错误:
偶发错误——拼写错误、缺少导入、忘记添加可见性修饰符、声明和使用时的类型名称不匹配。应立即修复并重新编译。这些只是干扰。
架构信号——这意味着模块映射有误:
- 模块间存在循环依赖 → 模块边界划分有误。停下来,返回模块映射。重新组织,直到依赖图成为有向无环图(DAG)。
- 边界处存在类型不匹配 → 接缝两侧对穿过边界的数据意见不一致。这是设计缺陷,而非代码缺陷。确定哪一侧正确,更新模块映射,然后修复代码。
- 无法在不访问其他模块内部实现的情况下实现某个协议/trait → 抽象边界位置不当。相关职责可能需要转移。
遇到架构信号时:停止编译,更新模块映射;如果改动较大,先征得用户确认,然后再继续。不要为了让编译器满意而绕过问题进行修补——一团乱麻就是这样开始的。
验证依赖方向
骨架编译通过后,验证声明的依赖规则是否成立。针对每一层:
Rust: 在每个模块目录中搜索 use 语句。每个 use crate::... 路径都只能引用同一层或更低层的模块。
# Example: verify Layer 2 modules only import from Layer 1
grep -rn "use crate::" core/capacitor-core/src/runtime/ | grep -v "domain"
# Should return nothing — any hit is a dependency violation
Swift: 检查导入语句。Swift 模块只能引用其声明的依赖项。
如果发现违规,这是架构缺陷。应修复模块映射,而非导入语句。
正确性标准
满足以下条件时,骨架才算“正确”:
- 每一层的构建都能通过,且错误数为零
- 每个模块的公共 API 都有完整的类型定义
- 每个协议/trait 至少有一个存根实现
- 已通过机械方式验证依赖方向(见上面的搜索检查)
- FFI 边界类型在两侧均已定义,且绑定层可以编译
记录验证结果:
## Skeleton Verification
- Rust (cargo check): PASS
- Swift (swift build): PASS
- FFI bindings: PASS (generated headers match Swift imports)
- Dependency direction: PASS (no violations found)
- Module count: 8 (4 Rust, 4 Swift)
- Protocol/trait count: 6
- Stub implementation count: 6
- FFI boundary types: 12 shared types verified
阶段 3:梳理旧代码库
有了经过验证的骨架后,现在回到现有代码,为其创建精确映射:新架构中的每个存根,其逻辑来自哪里?
从评估报告中的引用开始
如果评估报告列出了具体文件、函数和行号,就使用这些信息。不要重新搜索评估报告已经定位的代码。例如,如果评估报告指出 lib.rs:276-340 中的 CoreRuntime.initialize() 负责启动流程,那么它就是映射 RuntimeEngine.start() 的依据。
对于评估报告未指明具体代码的存根,再按职责搜索现有代码库。
迁移清单
在项目根目录创建 migration-manifest.md:
# Migration Manifest
Source assessment: [path to assessment]
Skeleton branch: architecture-scaffold
Generated: [date]
## RuntimeEngineImpl
### start(config:)
- **Source:** `CoreRuntime.initialize()` in `core/capacitor-core/src/lib.rs:276-340`
- **Action:** PORT
- **Confidence:** HIGH
- **Notes:** Currently also does setup validation; that moves to SetupService.
Signature change: takes RuntimeConfig instead of raw path + options.
### stop()
- **Source:** `CoreRuntime.shutdown()` in `core/capacitor-core/src/lib.rs:342-380`
- **Action:** PORT
- **Confidence:** HIGH
- **Notes:** Straightforward extraction, no entangled dependencies.
### currentSnapshot()
- **Source:** `CoreRuntime.get_snapshot()` in `core/capacitor-core/src/lib.rs:382-395`
- **Action:** ADAPT
- **Confidence:** HIGH
- **Notes:** Remove the global state bypass that check_hook_health uses.
The function is simple, but it currently reads from a global rather than
the instance's storage — that's the adaptation.
## SetupServiceImpl
### performSetup(config:)
- **Source:** `CoreRuntime.run_setup()` + `CoreRuntime.validate_config()`
- **Action:** REWRITE
- **Confidence:** MEDIUM
- **Notes:** Logic is scattered across the god object and interleaved with
runtime concerns. Gather requirements from both sources but write fresh.
Pay attention to the error handling in validate_config — it has edge cases
the rewrite needs to preserve.
操作类别
为每个存根分配且仅分配一种操作:
- 移植(PORT)——现有逻辑符合新签名的要求。提取它,调整签名并迁移。逻辑本身没有问题。
- 调整(ADAPT)——现有逻辑基本正确,但需要进行较大改动才能适配新架构(例如,移除对全局状态的依赖,或拆分一个承担两项职责的函数)。
- 重写(REWRITE)——现有逻辑耦合过于复杂、结构差异过大,或本身有误。以旧代码为参考,而非作为源材料,重新编写逻辑。
- 新增(NEW)——没有现有逻辑能满足需求。这是旧架构不具备的新能力。
- 删除(DELETE)——旧代码中存在该功能,但新架构中不再需要。请向用户确认它确实已不再使用。
置信度等级
为每项映射评定置信度:HIGH、MEDIUM 或 LOW。
- HIGH — 源代码位置明确,逻辑自成一体,操作直接。
- MEDIUM — 源代码位置明确,但逻辑存在依赖或副作用,需要谨慎处理。
- LOW — 不确定这是否是正确的源代码位置,或者逻辑纠缠复杂,可能导致操作分类错误。
应将低置信度映射标记出来,供用户审查。它们最有可能在迁移过程中引发问题。
提交清单供审查
向用户展示迁移清单。需要重点检查:
- PORT 与 REWRITE 的判断是否正确?(用户通常清楚代码库中哪些部分值得信任)
- 是否需要调查低置信度映射?
- 映射中是否遗漏了内容?
- 标记为“DELETE”的项目是否确实已废弃?
输出
完成后,用户将获得:
- 可编译的类型骨架:位于一个分支上,作为经验证的目标架构代码
- 模块映射:以易读的方式描述架构及依赖规则
- 迁移清单:从旧代码到新桩代码的精确映射,包含操作和置信度等级
- 编译器与依赖验证:以机械方式证明类型彼此兼容,且依赖关系流向正确
这些产物将作为下一阶段的输入:通过 architectural-refactor 技能或手动开发来实际实现。骨架分支将成为所有实现工作的目标,而迁移清单会明确告知 Agent 在何处找到所需逻辑。
衔接 architectural-refactor 技能
如果用户计划使用 architectural-refactor 技能执行迁移,则需要将迁移清单转换为该技能所需的格式。每项桩代码映射都会成为重构计划中的一个块:
- PORT 桩代码对应“提取并迁移”块(风险较低,应优先处理)
- ADAPT 桩代码对应“提取、修改并迁移”块(中等风险)
- REWRITE 桩代码对应“编写新实现”块(风险最高,应最后处理)
- DELETE 项目对应最后处理的清理块
- NEW 项目对应端口处理完毕后执行的实现块
将相关桩代码归为同一个块(例如,某个模块的所有 PORT 桩代码归为一个块)。按风险排序:先 PORT,再 ADAPT,最后 REWRITE。每个块的退出标准都应包含 Phase 2 中使用的相同编译检查——如果填入真实逻辑后骨架仍能编译,就说明架构没有偏离。
指导原则
- 编译器才是权威,而不是 Agent。 如果 Agent 认为架构合理,但编译器不认同,应始终以编译器为准。
- 先类型,后逻辑。 不要急着“顺手快速实现这个函数”。整个流程的核心是独立于行为验证结构。
- 架构错误不是编译错误。 当编译器揭示出结构问题(循环依赖、边界不匹配)时,应回到模块映射。不要通过修补代码来让编译器不再报错。
- 准确性优先于速度。 含糊的迁移清单(“逻辑在 lib.rs 的某处”)不但毫无用处,还会让 Agent 得以自行猜测。务必具体明确。
- 尽早提问,频繁提问。 在编写代码前解决每个歧义,可以减少一个数量级的返工。当规范不明确时,应停下来提问。
- 沿流程单向推进。 绘制映射时不要跳回去编写骨架。如果映射暴露出骨架缺陷,应明确返回 Phase 2,修复骨架、重新编译,然后再继续绘制映射。