# 生成代码的去向 > 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 不承诺自动合并到生产仓库。 ## ② 沙箱代码的生命周期 ## ③ Export API · v1 实现 ```ts // 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 模板 ```js # 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/ /services/ 2. 整理目录结构、合并 package.json 3. 跑 lint / 格式化(DevLoop 不强制风格) 4. 自己开 PR ## License 继承 LemonBoss 主仓库。 ``` ## ⑤ 沙箱代码的保留期 shipped 项目的 `units/` 默认**永久保留**,因为: - 占用极小(中项目 ~30MB 含日志) - 反推决策需要原始代码 + coder.log - memory 系统会引用具体 Unit 实现做风格参考 清理策略(写在 `project.json`): | 字段 | 默认 | 含义 | | --- | --- | --- | | `compressOldRuns` | false | 14 天前的 runs/*/stdout.txt 自动 gzip | | `keepCodeMonths` | 12 | shipped 12 个月后归档 code/ 到 history/archived-units/ | | `autoExportOnShip` | false | shipped 时自动生成 export.zip 放项目目录 | ## ⑥ v2 路径 · 自动 PR v2 会加 `POST .../export-to-github`,参数: ```js { "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](data-layout.html) · units/ 在文件系统里的位置 - [plan-migration](plan-migration.html) · 各 Phase 何时开始关心代码归宿 - [复现指南](plan-reproducibility.html) · 外部 Claude Code 怎么从沙箱代码 + docs 复现整套