08 · PLAN/Code Lifecycle

生成代码的去向

Coder 生成的 units/{id}/code/** shipped 后去哪?继续在沙箱里?合到主仓库?开 PR?本页给出 v1 决策与升级路径。

① 三种归宿

方案位置优劣
A · 留沙箱(v1 默认)data/companies/{cid}/projects/{pid}/units/**零集成成本;可被复现脚本读;隔离强不能直接运行在 LemonBoss 主进程;需手动导出
B · 手动 export 导出人类点「导出」按钮 → 下载 zip简单;人类可控;适合外部交付没有 git 历史;不接 CI/CD
C · 自动开 GitHub PR主仓库的 feature branch真集成;CI 自动跑;review 流程需 GitHub token / 权限;branch 命名冲突;merge 策略复杂
v1 决策:A + B。C 留到 v2 视真实需要再说。本套文档对外宣称"生成代码",但 v1 不承诺自动合并到生产仓库。

② 沙箱代码的生命周期

生成期Coder 写 测试期Tester 跑 集成期Integrator shippedv1 默认: 留这 manual export / archive 导出动作(人类触发) POST /api/devloop/.../export 打包成 zip 含全部 units/*/code/ 下载 / 解压 人类决定怎么集成 git merge / npm publish / Docker build

③ Export API · v1 实现

// server/routes/devloop.js · 新增
router.post('/projects/:pid/export', requireAuth, async (req, res) => {
  const { pid } = req.params;
  const { include = 'code' } = req.body;   // code / code+tests / all

  const archiver = require('archiver');
  res.setHeader('Content-Type', 'application/zip');
  res.setHeader('Content-Disposition', `attachment; filename="${pid}.zip"`);

  const zip = archiver('zip');
  zip.pipe(res);

  const root = projectsDir(pid);
  if (include.includes('code'))  zip.directory(path.join(root, 'units'),     'units');
  if (include.includes('tests')) zip.directory(path.join(root, 'integration'),'integration');
  if (include.includes('docs'))  {
    zip.directory(path.join(root, 'prd'), 'prd');
    zip.directory(path.join(root, 'sdd'), 'sdd');
  }

  // 附 README 说明项目结构
  zip.append(buildReadme(pid), { name: 'README.md' });
  await zip.finalize();
});

archiver 在 LemonBoss 既有依赖("archiver": "^7.0.1"),零新增包。

④ 导出包里的 README 模板

# Project {pid}

由 LemonBoss DevLoop v1 生成 · {timestamp}

## 结构
```
units/
├── todo-be/code/        后端代码
├── todo-be/tests/       Tester 写的 vitest
├── todo-be/contract.json  接口契约
├── todo-fe/...
integration/spec.json    端到端用例
prd/                     原始需求文档 (HTML 站点)
sdd/                     架构设计文档 (HTML 站点)
```

## 跑起来
```bash
cd units/todo-be
npm install        # 各 Unit 独立装包
PORT=3010 node code/server.js

# 另起前端 (如有)
cd units/todo-fe
npm install
npm run dev
```

## 测试
```bash
cd units/todo-be
vitest run         # 跑 Tester 写的 spec
```

## 集成
```bash
# 在项目根
node integration/runner.js   # 启动所有 be + 跑 spec.json
```

## 合到主仓库(手动)
没有自动 PR 流程。建议:
1. cp -r units/*/code/ <your-repo>/services/
2. 整理目录结构、合并 package.json
3. 跑 lint / 格式化(DevLoop 不强制风格)
4. 自己开 PR

## License
继承 LemonBoss 主仓库。

⑤ 沙箱代码的保留期

shipped 项目的 units/ 默认永久保留,因为:

  • 占用极小(中项目 ~30MB 含日志)
  • 反推决策需要原始代码 + coder.log
  • memory 系统会引用具体 Unit 实现做风格参考

清理策略(写在 project.json):

字段默认含义
compressOldRunsfalse14 天前的 runs/*/stdout.txt 自动 gzip
keepCodeMonths12shipped 12 个月后归档 code/ 到 history/archived-units/
autoExportOnShipfalseshipped 时自动生成 export.zip 放项目目录

⑥ v2 路径 · 自动 PR

v2 会加 POST .../export-to-github,参数:

{
  "repo": "yibo/lemonboss-services",
  "branch": "devloop/p-731042",
  "target_path": "services/todo/",
  "commit_message": "feat: add TODO API + UI (via DevLoop)",
  "open_pr": true,
  "github_token": "${secret}"
}

逻辑:调 gh CLI / @octokit/rest 创建 branch + 提交 + 开 PR。v1 不做。

⑦ 跨项目共享 utils

常见担忧:5 个项目都写一遍 JWT 中间件,沙箱内重复实现。v1 接受这个浪费;v2 视情况引入 data/companies/{cid}/shared/,约定:

  • Architect 检测多 Unit 共享 util 时,建议拆 shared-* Unit
  • shared Unit 的 contract.json 跨项目稳定
  • Coder 读 shared 时只读 contract,不复制实现

v1 简化:每个项目重复实现 utils。memory 系统会记"这次写了 JWT mw, 下次可以照样",但不会自动拆 shared。

⑧ 关联

  • data-layout · units/ 在文件系统里的位置
  • plan-migration · 各 Phase 何时开始关心代码归宿
  • 复现指南 · 外部 Claude Code 怎么从沙箱代码 + docs 复现整套