关于 forge-idiomatic-engineer
面向使用 Forge 全栈 Rust 框架的工程师,指导 Agent 熟悉项目、实现处理器并进行验证。内容涵盖编译流程、数据库迁移和弹性设计。
主要功能
- 01Forge 项目导览与环境检查
- 02处理器类型及注册指导
- 03使用 forge 命令生成与验证
- 04Rust、SQLx、迁移和前端参考
- 05弹性设计与故障处理原则
适用场景
- 01开始处理现有 Forge 项目
- 02添加并验证后端处理器
- 03排查 Forge 构建或迁移问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ isala404/ forge --skill forge-idiomatic-engineer安装后在 Claude Code 中输入/forge-idiomatic-engineer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdfrontend.mdpatterns.mdpitfalls.mdrecipes.mdresilience.mdtesting.mdreferences/frontend/dioxus.mdsvelte.mdscripts/orient.sh详细介绍
Forge 惯用工程师
全栈 Rust 框架。单个二进制文件,以 PostgreSQL 为后端。基于 Axum + Tokio + SQLx。宏会生成运行时连接代码和前端绑定;每个处理程序都必须在 src/main.rs 中注册(仅靠宏不会完成接入)。
编译循环硬性规则
如果忽略这些规则,调试可能会白白耗上数小时。编写代码前请牢记。
- 手动运行任何
cargo check/cargo build时,都必须设置SQLX_OFFLINE=true。 CI 会全局设置此变量。否则,sqlx 会尝试根据你当前的DATABASE_URL验证每个sqlx::query!(),包括你无法编辑的已发布forge-runtimecrate 文件中的查询,最终会在第三方代码中看到一大堆“column does not exist”错误。最简单的解决办法是在 shell rc 文件中运行eval "$(forge env)";否则就手动运行export SQLX_OFFLINE=true。 forge check会自动准备离线缓存。 它会检测src/是否比.sqlx/更新,并在流水线其余步骤运行前执行cargo sqlx prepare --workspace,因此你无需考虑 prepare 的执行顺序。(对于原始的cargo check,每次编辑任何sqlx::query!()后,仍需运行forge migrate prepare。)在 CI 中,如果缓存应已正确准备好,请传入--no-prepare。- 如果缺少
cargo-sqlx,forge migrate prepare会直接失败。 请运行cargo install sqlx-cli --no-default-features --features postgres安装,然后重试。 - 所有
forge命令都会向上查找forge.toml。 可在任意子目录中运行;启动时会打印解析出的根目录。无需先运行cd。 - 如果编译循环中的任何环节感觉不对,先运行
forge doctor。 它会一次性检查 rustc、cargo-sqlx、SQLX_OFFLINE、DATABASE_URL是否可访问、Docker、前端工具、forge.toml语法、.sqlx/是否为最新状态,以及最新迁移文件是否为空。 cargo sqlx prepare成功就意味着编译通过。 不要只为“确认”刚才 prepare 已证明的结果而运行forge check——prepare 内部会调用完整的cargo check。只有在上次 prepare 后有新改动,或要运行其余验证套件(注册、schema、clippy)时,才运行forge check。
会话开始:只读一次,相信记忆
先运行 bash docs/skills/forge-idiomatic-engineer/scripts/orient.sh。该脚本会向上查找 forge.toml,然后打印结构化信息:项目名称 + 认证模式 + 前端、环境就绪状态(SQLX_OFFLINE、DATABASE_URL、cargo-sqlx、Docker)、.sqlx/ 缓存是否为最新状态、src/main.rs / src/functions/mod.rs / src/schema/mod.rs 的内容、按类型分组的所有已注册处理程序、最新迁移、启用了响应式功能的表,以及具体的 NEXT 操作提示。运行一次即可取代五次单独读取。
每个会话都要阅读一次 references/pitfalls.md 和 references/resilience.md——脚本不会打印它们。
如果脚本不可用(检出版本较旧,或环境受沙箱限制),请改为阅读 forge.toml、Cargo.toml、src/main.rs、src/functions/mod.rs,以及 migrations/ 下最新的文件。
只有在你写入这些文件时,它们才会改变——在会话中途重新读取几乎总是在浪费上下文。
自动压缩后,相信摘要中的文件清单。不要为了确认摘要已记录的信息而重新读取 main.rs / mod.rs——只有在准备写入这些文件、需要确认其当前准确内容时才重新读取。如果压缩内容附带了 <attachment> 文件,就将其视为已在上下文中;不要再发起新的 Read。
确实需要读取文件时,一次调用就完整读取。不要读取有重叠的范围(先从 offset 200 读,再从 offset 1 读)——应从 offset 1 开始,用足够大的 limit 一次读取。Forge 处理程序文件很少超过 600 行。
处理器类型
| 概念 | 宏 | 结构体后缀 | 注册方式 |
|---|---|---|---|
| 只读查询 | #[forge::query] | Query | .register_query::<FnNameQuery>() |
| 数据变更 | #[forge::mutation] | Mutation | .register_mutation::<FnNameMutation>() |
| 后台任务 | #[forge::job] | Job | .register_job::<FnNameJob>() |
| 定时任务 | #[forge::cron] | Cron | .register_cron::<FnNameCron>() |
| 持久化工作流 | #[forge::workflow] | Workflow | .register_workflow::<FnNameWorkflow>() |
| 长时间运行的进程 | #[forge::daemon] | Daemon | .register_daemon::<FnNameDaemon>() |
| 外部 HTTP 事件 | #[forge::webhook] | Webhook | .register_webhook::<FnNameWebhook>() |
| AI Agent 工具 | #[forge::mcp_tool] | McpTool | .register_mcp_tool::<FnNameMcpTool>() |
也可以使用 .auto_register(),通过 inventory 自动注册所有处理器。
命名规则
- 仅支持
pub async fn处理器——私有函数会导致代码生成失败。 snake_case函数名 → 宏会生成PascalCase+ 类型后缀。函数名中不要包含类型名(用heartbeat,而不是heartbeat_daemon,否则会得到HeartbeatDaemonDaemon)。#[forge::model]必须是结构体上的第一个属性。
Context API 速览
请记住这些内容,以免在实现过程中查阅 forge-core 源码:
ctx.db()→ForgeDb(一个sqlx::Executor)。直接传给查询宏:sqlx::query_as!(...).fetch_one(ctx.db()).await?。ctx.conn().await?→ForgeConn<'_>(事务型,仅用于变更)。将&mut conn传给查询宏。ctx.user_id()→QueryContext和MutationContext上的Result<Uuid>。如果没有主体,则返回ForgeError::Unauthorized。MutationContext上没有ctx.auth()方法。ctx.db_conn()→DbConn<'_>,用于必须同时适用于查询和变更的共享辅助函数。DbConn的调用约定相反(在DbConn上调用.fetch_*,并传入查询)——请参阅references/patterns.md。- 让类型推断确定绑定的类型(
let mut conn = ctx.conn().await?)。除非辅助函数签名有要求,否则不要导入ForgeConn/ForgeDb/DbConn并显式声明类型。
工作流
- 了解项目——阅读上面的会话启动文件列表,以及
references/pitfalls.md和references/resilience.md。通过frontend/package.json(Svelte)或frontend/Cargo.toml(Dioxus)判断前端类型。 - 规划改动范围——在编写代码前,确定此 PR 要包含哪些处理器、迁移和前端改动。改动应精简、纵向贯穿,聚焦于单个功能。
- 逐个处理器执行检查点循环:
- 使用
forge new <kind> <name>,不要手动编写文件。它会搭建包含正确宏默认值的框架,将pub mod <name>;追加到src/functions/mod.rs,并在src/main.rs缺少mod functions;时插入该声明。类型:query、mutation、job、cron、workflow、daemon、webhook、mcp_tool、model、enum。 - 编辑搭建好的文件:用实际实现替换占位 SQL / 业务逻辑。
- 运行
forge check。如果源文件比.sqlx/缓存新,它会自动准备缓存,因此通常不需要单独运行forge migrate prepare。 - 如果失败,修复根本原因并仅重新运行失败的步骤。还有错误时,不要编写下一个处理器。
- 继续处理下一个处理器。
- 使用
forge generate——后端改动稳定后运行。绝不要编辑生成的文件。- 前端——使用生成的绑定接入 UI。运行
forge test执行 Playwright E2E 测试。 - 最终检查——确保
forge check无错误、forge test全部通过,并撰写简短的改动摘要。
如果用户在你诊断问题后说“修复它” / “你能修复它吗”,就直接修复——不要请求再次确认。只有在执行破坏性操作(删除数据、删除架构、强制推送)时才暂停并确认。
架构默认项(提前选择)
- 身份验证:社交 OAuth、密码 + HS256 或 RS256——编码前选定(参见
patterns.md)。社交登录必须通过user_identities表关联。 - 环境变量:使用
ctx.env_require()/ctx.env_or(),绝不使用std::env::var()。 - HTTP:RPC 使用
ctx.http();需要bytes_stream()或自定义重定向策略时使用ctx.raw_http()。 - SQL:只使用
sqlx::query!()/query_as!()这类带感叹号的宏。架构或查询变更后运行forge migrate prepare——参见“编译循环硬性规则”。 - 作业/工作流:仅在变更操作中分派(默认启用事务)。切勿在会分派任务的变更操作上设置
transactional = false。 - 共享逻辑:一旦有两个处理器需要,就提取到
src/utils/。
参考资料选择指南
| 任务 | 参考资料 |
|---|---|
| 任何新处理器(必读) | references/resilience.md |
| 宏、上下文、错误、配置、CLI | references/api.md |
| 后端模式:作业、工作流、身份验证、Webhook、编译循环 | references/patterns.md |
| 可复制粘贴的示例(用户获取、套餐限制、电子邮件、S3、支付、AI) | references/recipes.md |
| 前端原则(响应性、订阅、错误、上传) | references/frontend.md |
| SvelteKit 细节(runes、stores、身份验证辅助工具) | references/frontend/svelte.md |
| Dioxus 细节(钩子、信号、身份验证键控) | references/frontend/dioxus.md |
| 编写测试(后端构建器 + Playwright 场景) | references/testing.md |
| 调试构建或运行时错误 | references/pitfalls.md |
工程原则
- 为故障而设计:身份验证可能中断、实体可能消失、网络可能故障。参见
resilience.md。 - 零死代码:彻底删除未使用的代码和已替换的模式。工作区 lint 会禁止
dead_code、unwrap_used、panic、indexing_slicing、unsafe_code。 - 精简差异:每个 PR 只包含一个精简的纵向切片。
- 边界验证:在处理器入口处验证,并返回
ForgeError变体(绝不使用unwrap/expect/panic!)。 - 范围强制执行**(由宏在编译时强制执行):私有查询必须按
user_id/owner_id过滤。对于主体,使用ctx.user_id()。仅对共享/管理员数据使用#[query(unscoped)]退出范围限制。 - 事务性分派(由编译时强制执行):
dispatch_job和start_workflow要求使用事务性变更操作。由于默认启用事务,只需确保不要在会分派任务的变更操作上设置transactional = false。 - 禁止访问系统表(由
forge check强制执行):绝不对forge_*表执行INSERT/UPDATE/DELETE。请使用dispatch_job、start_workflow、record_signal。 - 迁移:仅允许向前迁移——不要使用
-- @down。可选的-- @up标记会被移除。使用SELECT forge_enable_reactivity('table_name');启用响应性。不要使用IF NOT EXISTS。 - 未找到处理:
fetch_optional().await?.ok_or_else(|| ForgeError::NotFound(format!(...)))。
输出要求
完成后,报告:
- 变更:修改的文件、处理的故障模式、新增的测试(包括故障路径)。
- 验证:运行的命令、阻碍因素、最终
forge check结果。 - 审查:韧性方面的缺口、假设。
每个处理器都必须能应对身份验证被撤销、实体被删除、并发修改和网络中断。