#AI Agent#TypeScript#DevOps#开源

生成代码的所有权边界:源文件、产物与再生成如何不互相覆盖

直接修生成产物通常会在下一次 codegen 消失。本文用所有权清单、确定性再生成、扩展层和原子发布定义谁能改源、谁能改模板,以及如何安全回滚整套制品。

本文要点

  • 主题聚焦:AI Agent、TypeScript、DevOps、开源
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年4月22日·更新于 2026年7月20日

仓库里的 src/generated 看起来和普通源码一样,编程 Agent 看到一个错误时也很容易直接修改。下一次 codegen 会把补丁覆盖;更糟的是,团队可能只提交了产物,没有提交 schema 或生成器升级,导致任何人都无法重建同一结果。生成代码必须有明确所有权:人修改源与模板,生成器拥有产物,扩展逻辑进入不会被覆盖的边界。

仓库地图应标出生成路径,编程 Agent 的仓库地图提供入口;本文进一步定义一份可由 CI 执行的所有权契约。

本文命令固定在 Linux/GNU CI 容器(Bash、GNU coreutils、Git)执行;macOS/Windows runner 必须使用同一容器。

用生成清单连接源与产物

每个生成目标登记输入、模板、生成器、参数和输出,而不是只在 README 留一条命令。生成器用容器 digest 或 lockfile 中的精确包版本表示;远程 schema 先固定内容 digest,不能在构建时读取会变化的 latest URL。

yaml
target: payments-ts-client
inputs:
  - path: api/payments.openapi.yaml
    sha256: 9e31...
templates:
  path: tools/openapi-templates
  sha256: b21a...
generator:
  image: registry.example/openapi-generator@sha256:4ac8...
outputs:
  - packages/payments-client/src/generated
owners: [payments-platform]

产物头包含类似 Code generated ... DO NOT EDIT. 的标记、目标 ID 和生成清单 digest,但不写当前时间或本机绝对路径。Go 的生成约定把 go generate 作为显式开发动作,并不在普通构建中自动运行;同样的原则适用于其他语言:何时再生成必须由版本化命令决定。

人工扩展不能藏在覆盖区

自定义行为放在 adapter、partial class、接口实现或生成器支持的模板层。OpenAPI Generator 的 ignore 文件可以阻止某些文件覆盖,但每个例外都要说明真正 owner;大量 ignore 往往意味着边界设计错误。若某个紧急补丁只能落在产物中,状态应是限时例外,随后回补源或模板,并用再生成证明补丁不会消失。

Agent 修改生成目录前,CI 反查清单:如果 diff 没有同时包含对应输入、模板或生成器变化,默认阻断。反过来,源发生变化却没有预期产物 diff,也要阻断,防止作者忘记再生成。对 API 客户端还需接入Agent 改 API 的契约兼容门,因为“成功生成”不代表旧消费者兼容。

在干净工作树证明确定性

再生成检查不能在开发者已有产物和缓存上执行。CI 创建隔离检出,安装冻结依赖,运行唯一入口,再要求工作树与提交一致:

bash
set -euo pipefail
TMP_ROOT=$(mktemp -d)
CHECKOUT="$TMP_ROOT/checkout"
cleanup() {
  git worktree remove --force "$CHECKOUT" >/dev/null 2>&1 || true
  rmdir "$TMP_ROOT" >/dev/null 2>&1 || true
}
trap cleanup EXIT

git worktree add --detach "$CHECKOUT" HEAD
(
  cd "$CHECKOUT"
  GENERATED_PATHSPEC=':(glob)packages/*/src/generated/**'
  pnpm install --frozen-lockfile
  pnpm codegen
  git diff --exit-code HEAD -- "$GENERATED_PATHSPEC"
  test -z "$(git ls-files --deleted -- "$GENERATED_PATHSPEC")"
  test -z "$(git ls-files --others --exclude-standard -- \
    "$GENERATED_PATHSPEC")"
)

cleanup
trap - EXIT

状态检查同时覆盖已跟踪修改和新产物。若第一次生成有 diff,说明提交缺产物或工具版本不一致;连续生成两次还有 diff,则检查时间戳、随机排序、路径分隔符和非固定网络输入。不能把 CI 生成后的文件自动提交回分支来掩盖不可复现性。

生成门还要校验文件集合。schema 删除类型后,旧产物必须被删除;只覆盖已有文件会留下幽灵 API。清单记录前一次输出列表,新一轮以暂存目录生成,验证完成后原子替换目标目录,避免中途失败留下新旧混合状态。

评审源变化,也评审产物风险

大 diff 不应迫使 reviewer 逐行阅读机器样板,但也不能完全折叠。报告汇总新增/删除公共符号、协议字段、依赖和文件数,并抽样展示高风险输出。生成器镜像与模板属于供应链制品,要执行签名、来源和漏洞检查,相关边界可结合AI 供应链安全。

发布前在支持的语言与运行时矩阵构建生成客户端,运行序列化、错误响应和旧服务兼容测试。生成器升级与 schema 业务变化最好拆成两个提交:前者证明相同输入的机械差异,后者展示真实契约变化。混在一起会让回归无法归因。

回滚必须恢复完整生成元组

正确回滚单位是 输入 + 模板 + 生成器 + 参数 + 产物。线上出现问题时切回上一个已验证制品 digest,并反向提交整个元组;只恢复产物会让下一次生成再次引入问题,只恢复 schema 则可能让当前二进制和客户端不匹配。

在隔离的回滚分支中执行并复验完整元组:

bash
git revert --no-edit "$CODEGEN_CHANGE_SHA"
GENERATED_PATHSPEC=':(glob)packages/*/src/generated/**'
pnpm install --frozen-lockfile
pnpm codegen
git diff --exit-code HEAD -- "$GENERATED_PATHSPEC"
test -z "$(git ls-files --deleted -- "$GENERATED_PATHSPEC")"
test -z "$(git ls-files --others --exclude-standard -- \
  "$GENERATED_PATHSPEC")"
pnpm build

恢复后在干净检出重跑 pnpm codegen 与构建,要求零 diff,并检查旧客户端或服务仍能互通。生成清单、验证报告和回滚 digest 作为CI/CD 流水线设计的发布工件保留。清晰的所有权边界最终让 Agent 知道该改哪里,也让任何产物都能回答“由什么、用哪个版本、在何种参数下生成”。

Related

相关推荐