mcpField
A
architecture-scaffold

architecture-scaffold

github.com/petekp开发4849 次安装2 天前更新
代码仓库

关于 architecture-scaffold

architecture-scaffold 是将高层架构规范转为可编译类型骨架的 Agent 技能,在加入实现逻辑前验证结构。它涵盖模块映射、分层编译、依赖检查,以及将现有代码映射到新结构。

主要功能

  1. 01提取模块、类型与签名
  2. 02定义分层依赖规则
  3. 03逐层构建类型桩
  4. 04编译并验证依赖方向
  5. 05将现有逻辑映射到骨架

适用场景

  1. 01依据架构规范搭建重构骨架
  2. 02通过编译反馈检查模块边界
  3. 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. 类型是硬性约束——编译器会在构建时拒绝违规内容
  2. 测试是行为验证——确认代码在运行时的行为
  3. 规范是软性指导——提供参考,但无法强制执行

此技能作用于第 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”的项目是否确实已废弃?

输出

完成后,用户将获得:

  1. 可编译的类型骨架:位于一个分支上,作为经验证的目标架构代码
  2. 模块映射:以易读的方式描述架构及依赖规则
  3. 迁移清单:从旧代码到新桩代码的精确映射,包含操作和置信度等级
  4. 编译器与依赖验证:以机械方式证明类型彼此兼容,且依赖关系流向正确

这些产物将作为下一阶段的输入:通过 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,修复骨架、重新编译,然后再继续绘制映射。

评论

支持 Markdown