diff --git a/.claude/commands/dev-flow.md b/.claude/commands/dev-flow.md index f6902b8a07de2dd53a23022a50625ac6c5ef20dc..114d417d87cb05518d1af703ea998d9656feb872 100644 --- a/.claude/commands/dev-flow.md +++ b/.claude/commands/dev-flow.md @@ -7,7 +7,16 @@ author: DevSyncAgent Team last_updated: 2026-07-17 changelog: v4.9 - 2026-07-17 - - 🛡️ 版本全流程进度一致性修复:继续版本开发步骤2新增双存储点一致性校验(versions.json currentStage vs context.md currentStage 不一致时以 stageHistory 较长者为准回写对齐),消除双存储点漂移隐患 + - 🔁 Stage 9 循环决策改为可配置:新增独立 `loop_decision.mode=auto|ask`,标准/质量模板默认询问,极简模板保持自动;不再复用仅控制阶段进入确认的 `non_skippable.9` + - ⏸️ `ask` 模式检测到测试失败时通过 AskUserQuestion 询问“进入下一轮修复/暂停保留现场”;暂停保持 currentStage=9 且不完成 Stage 9,恢复后重新询问 + - 🧩 交互模板与运行时配置 schema 升至1.1;兼容1.0旧配置,无配置/解析失败/非法值安全回退为询问 + - 🧭 版本全流程上下文传递优化:新增 `.claude/config/dev-flow-context-contract.json`,为 21 个阶段定义 consume/produce 白名单与统一 stage result 协议 + - 🔄 新增版本级 `version-context.md` + 需求级 `context.md` 双层上下文;阶段 Prompt 每次实时刷新版本快照、上一阶段摘要、产物索引、决策与风险 + - 🛡️ 新增两阶段 checkpoint:所有完成/跳过/自动跳过/降级分支统一提交 stageOutputs + stageHistory,修复提前 CONTINUE 导致上下文和进度未传递 + - ♻️ resume 改为 checkpointId/state 恢复,废弃“只比较 stageHistory 长度”的模糊覆盖;Stage 10/complete-version 消费版本聚合上下文 + - 🔧 `stage-names.json` v1.1 补齐 Stage 10,确保 STAGE_ORDER、中文映射与上下文契约均为21阶段 + - ⛔ Claude Code 边界:Stage 4 和版本归档显式排除根目录 `.agents/`,禁止 `git add .`/`git add -A` + - 🛡️ 版本全流程进度一致性修复:在原 currentStage 双写基础上升级为 checkpointId/checkpointState 对账,避免仅按 stageHistory 长度推断权威状态 - 🔗 配套修改:stage-hooks.md 步骤1 改为双写一致(versions.json + context.md currentStage 同步写入)、business_api.go 后端聚合 version 状态、message_dispatcher.py create_task stage 恢复非空、task-viewer 前端 STAGE_PIPELINE 补 key + 启动中显示 - ⚠️ 版本号天花板约束:保持 v4.9(天花板=4.9),仅追加 changelog 条目 v4.9 - 2026-07-14(天花板回退至4.9,原 v4.11/v4.10 changelog 合并) @@ -947,14 +956,14 @@ Skill(version-change-order-generator, args={"versionPlanId": selected_id}) **概要**:幂等性检查→关联需求前置→启动确认→P0-P7前置步骤→并行启动(步骤A-F)→逐阶段Subagent启动(步骤E)。 -#### 17阶段推进与阶段后置动作 +#### 21阶段推进与阶段后置动作 > ⚠️ **详细执行流程已外置,禁止凭记忆执行!** > 执行此操作前,**必须** Read 以下清单文件后按步骤执行: > 📄 `.claude/config/dev-flow-checklists/stage-hooks.md` > ⛔ 禁止跳过Read直接执行。每个步骤执行完毕后必须逐项验证。 -**概要**:包含STAGE_ORDER映射表、阶段Prompt构造规则、Stage1-9后置动作(B1-B7.5,含DPMS同步、测试用例同步等)、safe_call_mcp容错机制、BIZ API同步规则。 +**概要**:包含21阶段 STAGE_ORDER、统一上下文契约、stage result/checkpoint、阶段Prompt构造规则、Stage1-10后置动作、safe_call_mcp容错机制和BIZ API同步规则。 ### 继续版本开发 **恢复已启动版本中各需求的后续阶段。版本名从当前活跃版本自动获取。** @@ -969,24 +978,27 @@ Skill(version-change-order-generator, args={"versionPlanId": selected_id}) #### 步骤2:读取各需求的当前阶段 对每个需求,**优先从 versions.json 的 currentStage 字段读取**;若该字段不存在,回退到产物扫描判定。 -⚠️ **双存储点一致性校验**(⭐v4.9 保证研发流程层进度严格一致): -版本模式下 currentStage 同时存在于 versions.json(权威源)和 context.md(同步镜像)。读取时必须校验两者一致: +⚠️ **三层上下文与 checkpoint 恢复校验**(⭐v4.9): +版本模式使用 versions.json(控制权威源)+ context.md(需求执行上下文)+ version-context.md(版本聚合上下文)。恢复前必须 Read `.claude/config/dev-flow-context-contract.json` 并执行: ``` -v_stage = versions.json 该需求 currentStage -c_stage = context.md YAML frontmatter currentStage -IF v_stage 存在 AND c_stage 存在 AND v_stage != c_stage THEN - # 不一致(历史漂移):以 stageHistory 较长者为准,回写对齐另一处 - v_hist_len = len(versions.json 该需求 stageHistory) - c_hist_len = len(context.md stageHistory) - authoritative_stage = v_stage IF v_hist_len >= c_hist_len ELSE c_stage - 回写 versions.json currentStage = authoritative_stage - 回写 context.md currentStage = authoritative_stage - OUTPUT: "⚠️ 检测到 currentStage 不一致(versions.json={v_stage}, context.md={c_stage}),已以 stageHistory 较长者 {authoritative_stage} 为准对齐" -ELIF v_stage 存在 AND c_stage 不存在 THEN - 回写 context.md currentStage = v_stage # 补齐镜像 -ELIF c_stage 存在 AND v_stage 不存在 THEN - 回写 versions.json currentStage = c_stage # 补齐权威源 +校验 immutable_identity_fields(versionName/taskName/reqIndex/reqPrefix) +IF 任一冲突 THEN 中止恢复并输出冲突字段,禁止静默覆盖 + +v_checkpoint = versions.json 该需求 checkpointId +c_checkpoint = context.md.checkpointId +IF context.md.checkpointState == "pending" AND c_checkpoint == v_checkpoint THEN + # 权威控制面已提交,只缺镜像 finalize + 按 versions.json 回写 context.md stageHistory/currentStage/lastCompletedStage/contextRevision + 更新 version-context.md requirementContextIndex + context.md.checkpointState = "clean" +ELIF context.md.checkpointState == "pending" AND c_checkpoint != v_checkpoint THEN + # 阶段结果已prepare但控制面未提交 + 不推进;保留候选 stageOutputs,重新执行当前阶段或由用户确认后调用 commit_stage_transition +ELIF versions.json 已推进但 context.md 缺对应checkpoint THEN + 从 versions.json + 实际产物扫描重建最小 stageOutputs,标记 recovered_from_artifacts,再finalize END IF + +最后刷新 version-context.md 的版本快照和 requirementContextIndex;禁止使用“stageHistory 较长者覆盖另一处”的旧策略。 ``` ``` @@ -1034,7 +1046,7 @@ END IF - 若无,创建新的(同"启动版本需求"步骤B) 3. **按STAGE_ORDER逐阶段恢复**(与"启动版本需求"步骤E相同的逐阶段控制逻辑): - - 从 versions.json 读取 currentStage,确定STAGE_ORDER中的恢复起点 + - 先完成 checkpoint 恢复,再从 versions.json 读取 currentStage/lastCompletedStage 确定恢复起点 - 从恢复起点开始,按STAGE_ORDER顺序逐阶段启动subagent - 每个阶段的Agent/Skill从"阶段Agent映射表"获取 - 可跳过环节沿用skipDecisions历史决策或AskUserQuestion询问 @@ -1043,9 +1055,10 @@ END IF 4. **⚠️ 禁止**:恢复时禁止启动 req-type-classifier 做全流程编排(与"启动版本需求"Step E相同规则) **注意**: -- 恢复时每个阶段prompt必须包含【已有产物路径】,避免重复生成已有文档 +- 恢复与首次启动必须共用 stage-hooks.md 的 stage_context 构造逻辑,每阶段重新读取三层上下文 +- 每个阶段prompt必须包含契约允许的上一阶段摘要、已有产物、已确认决策和未关闭风险,避免只恢复进度不恢复语义 - 每次恢复都会创建新的 biz session,记录本次交互历史 -- 恢复完成后更新 versions.json 的 currentStage 和 stageHistory +- 每个阶段完成/跳过后调用 commit_stage_transition,禁止直接单写 currentStage/stageHistory ### 回滚到指定阶段 diff --git a/.claude/config/dev-flow-checklists/complete-version.md b/.claude/config/dev-flow-checklists/complete-version.md index b055f0d67a769730ee65e8b800c3e8fad8642fbd..34b1441148b05f8e4f1e2a8416a5608df1022bff 100644 --- a/.claude/config/dev-flow-checklists/complete-version.md +++ b/.claude/config/dev-flow-checklists/complete-version.md @@ -5,13 +5,17 @@ ## 前置条件 -版本下所有需求均已完成16阶段全流程(目录已在 `dev/versions/{versionId}/completed/{task}/` 下)。 +版本下所有需求均已完成需求级 Stage 0-9,且 Stage 10 已完成或已按规则跳过(目录已在 `dev/versions/{versionId}/completed/{task}/` 下)。 ## 步骤清单(严格按顺序执行) 1. **完成状态检查**: + - Read `.claude/config/dev-flow-context-contract.json` + - 读取 `dev/versions/{versionId}/version-context.md`,确认 `checkpointState == "clean"`;pending 时先按 stage-hooks.md 恢复规则处理 - 扫描 `dev/versions/{versionId}/completed/` 目录下的所有需求 - - 确认每个需求的 context.md 状态为"已完成" + - 按 version-context.md 的 requirementContextIndex 定位每个需求 context.md + - 确认 immutable identity 一致、`checkpointState == "clean"`、`lastCompletedStage == 9`、`testResult == "test_passed"` + - 校验 versions.json/context.md 的 checkpointId/contextRevision 一致;禁止只比较 stageHistory 长度 - IF 存在未完成需求 THEN 输出未完成列表,终止流程 2. **用户确认(3选项)**:使用 AskUserQuestion 询问用户: @@ -339,12 +343,17 @@ ``` 9. **生成版本总结报告**: - - 汇总所有需求的文档和执行结果 + - 以 version-context.md.requirementContextIndex 为索引,汇总所有需求 context.md 的 stageOutputs/artifactIndex/decisionLog/openRisks + - 对每个引用产物执行存在性校验;缺失产物写入报告的“上下文完整性风险”,禁止静默忽略 + - 汇总 Stage 10 的 stage10Result(执行、跳过或失败原因) - 包含A6系列操作结果摘要 - 写入 `docs/{versionName}/版本总结报告.md` + - 报告生成后更新 version-context.md:`versionSummaryPath/versionStatus="ready_to_archive"/updatedAt` 10. **移动到归档目录**: - `dev/versions/{versionId}/` → `dev/completed/{versionId}/` + - version-context.md 随版本目录一起归档;移动后回写 versions.json.versionContextPath 为 `dev/completed/{versionId}/version-context.md` + - 将 requirementContextIndex 中每个 contextPath 原子改写为 completed 路径,并再次校验文件存在 - 更新版本配置状态为 `completed` 11. **版本归档Git提交**: @@ -395,6 +404,13 @@ # 11d. 执行Git提交 add_paths = [f FOR f IN filtered_files] + # Claude Code 项目边界:根目录 .agents 永远不属于版本归档 + add_paths = [f FOR f IN add_paths IF NOT (f == ".agents" OR f STARTS_WITH ".agents/")] + IF staged_files 中存在 ".agents/" THEN + 从暂存区移除 .agents/,保留工作区文件不变 + OUTPUT: "⚠️ 已从暂存区排除 .agents/(Claude Code 项目不提交该目录)" + END IF + # 禁止 git add . / git add -A,只按过滤后的显式路径暂存 # 路径加引号防止空格问题 quoted_paths = ' '.join([f'"{p}"' FOR p IN add_paths]) bash(f"git add {quoted_paths}") @@ -459,6 +475,7 @@ FOR operation IN [ ## ⛔ 完成验证(回复结束前必须逐项确认) - [ ] 步骤1: 状态检查 = ? (通过/未通过) +- [ ] 步骤1: version/requirement context checkpoint 均为 clean 且身份一致 = ? - [ ] 步骤2: 用户确认选项 = ? - [ ] 步骤2.5: A5.2前置检查 = ? (stage10Completed=True通过/补执行Stage 10/跳过+pending) - [ ] 步骤3: A6确认 = ? (执行/跳过) @@ -471,7 +488,9 @@ FOR operation IN [ - [ ] 步骤8.5: Business API Version状态更新 = ? - [ ] 步骤9: 总结报告已生成 = ? - [ ] 步骤10: 归档完成 = ? +- [ ] 步骤10: versionContextPath/requirementContextIndex 已切换到 completed 路径 = ? - [ ] 步骤11: 版本归档Git提交 = ? (提交成功/跳过无变更) +- [ ] 步骤11: `.agents/` 未修改、未暂存、未提交 = ? - [ ] 步骤11d: git commit = ? - [ ] 步骤11e: git push = ? (已推送/暂不推送/推送失败) diff --git a/.claude/config/dev-flow-checklists/create-version.md b/.claude/config/dev-flow-checklists/create-version.md index 67a398845d2c7ad34da7cb64af5c3c69c2b5df15..66f6c8b27795994cde1e9666ef182e3be9bee988 100644 --- a/.claude/config/dev-flow-checklists/create-version.md +++ b/.claude/config/dev-flow-checklists/create-version.md @@ -60,9 +60,39 @@ "testReportId": null, // 🆕 v4.8 测试报告ID,初始null,由 complete-version A6-Step3 写入 "branchName": "{步骤6.5创建的Git分支名,未创建时为null}", "pipelineId": "{步骤6.6输入的流水线ID,未执行时为null}", - "pipelineVersion": "{步骤6.6输入的流水线版本号,未执行时为null}" + "pipelineVersion": "{步骤6.6输入的流水线版本号,未执行时为null}", + "contextSchemaVersion": "1.0", + "versionContextPath": "dev/versions/{versionName}/version-context.md" } + ``` + - 初始化版本级上下文 `dev/versions/{versionName}/version-context.md`。该文件是执行期聚合视图,`versions.json` 仍是生命周期与控制字段权威源: + ```markdown + --- + contextSchemaVersion: "1.0" + contextRevision: 1 + versionName: "{versionName}" + versionStatus: "planning" + productId: "{productId}" + productName: "{productName}" + subsystemId: "{subsystemId}" + subsystemName: "{subsystemName}" + subsystemVersionId: "{subsystemVersionId}" + testSetId: "{testSetId}" + regressionTestSetId: "{regressionTestSetId}" + branchName: "{branchName}" + pipelineId: "{pipelineId}" + pipelineVersion: "{pipelineVersion}" + requirementContextIndex: {} + openRisks: [] + stage10Result: {} + updatedAt: "{ISO 8601时间}" + --- + + # 版本上下文 + + 本文件由 dev-flow version 主会话维护。禁止 Agent/Skill 直接修改控制字段。 ``` + - 创建/更新该文件前必须读取 `.claude/config/dev-flow-context-contract.json`;若文件已存在,只刷新版本快照字段,保留 `requirementContextIndex/openRisks/stage10Result`。 6.5 **创建版本 Git 分支**: ```python # 基于第1.5轮收集的 subsystemVersionId 生成建议分支名 @@ -98,6 +128,7 @@ # 将分支名写入 versions.json 当前版本配置 更新当前版本配置:branchName = branch_name + 同步刷新 version-context.md:branchName = branch_name, contextRevision += 1, updatedAt = now ``` ⚠️ **分支名规范**:建议采用 `dev-{subsystemVersionId}` 格式(如 `dev-12.23.34`),便于版本追溯。 @@ -174,6 +205,7 @@ IF result["success"] THEN OUTPUT: f"✅ 步骤6.6 已完成:流水线已更新(pipelineId: {pipeline_params.pipelineId}, version: {pipeline_version})" 更新当前版本配置:pipelineId = pipeline_params.pipelineId, pipelineVersion = pipeline_version + 同步刷新 version-context.md:pipelineId/pipelineVersion/contextRevision/updatedAt ELSE mcp__biz-sync__save_pending_item( version_id="{versionName}", @@ -287,7 +319,8 @@ END IF - [ ] 步骤4: A2b 调用结果 = ? - [ ] 步骤5: A5 testPlanId = ? - [ ] 步骤5.1: A5.1 regressionTestSetId = ? (成功/跳过+pending) -- [ ] 步骤6: versions.json 已写入 = ?(含 regressionTestSetId 字段) +- [ ] 步骤6: versions.json 已写入 = ?(含 regressionTestSetId/contextSchemaVersion/versionContextPath) +- [ ] 步骤6: version-context.md 已初始化/刷新 = ? - [ ] 步骤6.5: 分支名 = ? - [ ] 步骤6.6: 决策选项 = ? (执行更新流水线/跳过) - [ ] 步骤6.6: 更新流水线 = ? (成功/失败+pending/跳过+pending/字段缺失+pending) diff --git a/.claude/config/dev-flow-checklists/rollback.md b/.claude/config/dev-flow-checklists/rollback.md index 52e67d60f8c33e5a32f56c37ea90935c7313a37c..deb1e3cc1e0eec19f040b43b9cd663f6c2ebeb5f 100644 --- a/.claude/config/dev-flow-checklists/rollback.md +++ b/.claude/config/dev-flow-checklists/rollback.md @@ -6,7 +6,7 @@ ## 核心理念 -**stage 级回滚 = 修改 currentStage + 清空目标 stage 及之后的 stageHistory/skipDecisions + 记录 rollbackHistory,然后按新 currentStage 继续 dev-flow 流程。** +**stage 级回滚 = 修改 currentStage + 清空目标 stage 及之后的控制状态/阶段输出 + 标记失效产物 + 记录 rollbackHistory,然后按新 currentStage 继续 dev-flow 流程。** - 粒度:stage 级(不搞 MCP 调用级/step 级补偿事务) - 语义:回滚到 stage X = 从 X 重新执行(X 之前的产物保留,X 及之后清空重做) @@ -15,17 +15,20 @@ ## 触发入口 -> ⚠️ 主菜单"回滚到指定阶段"选项 + stage 失败决策"回滚到指定阶段"第四选项已屏蔽(不展现给用户)。当前仅保留流程内自动回滚点。 +> ⚠️ 主菜单"回滚到指定阶段"选项 + stage 失败决策"回滚到指定阶段"第四选项已屏蔽(不展现给用户)。Stage 9 的询问只决定是否进入修复循环,不开放回滚目标选择;一旦进入循环,仍执行固定 target=1 的自动回滚。 | 入口 | 模式 | 说明 | |------|------|------| | Stage5 部署失败 | auto (target=3) | 保留原语义:部署失败回代码开发;改走统一函数修复 stageHistory 遗漏 | | Stage3.2 自检阻断"返回修复" | auto (target=3) | 保留原语义;改走统一函数 | -| Stage9 测试失败循环 | auto (target=1) | 保留原语义:自动强制回 stage1 + 生成 bug fix 子需求;改走统一函数,MAX_CYCLES=10 不变 | +| Stage9 测试失败循环 | policy(auto/ask) → auto (target=1) | `loop_decision.mode` 决定自动进入或先询问;确认进入后生成 bug fix 子需求并固定回滚到 stage1,MAX_CYCLES=10 不变 | ## 前置:读取中文映射 -执行回滚前,**必须** Read `.claude/config/dev-flow-checklists/stage-names.json` 获取 `stage_names`(stage->中文名)和 `stage_order`(STAGE_ORDER 有序列表)。 +执行回滚前,**必须** Read: + +- `.claude/config/dev-flow-checklists/stage-names.json`:获取 `stage_names` 和 `stage_order` +- `.claude/config/dev-flow-context-contract.json`:获取 controller_only_fields、阶段产出契约与 checkpoint 规则 ## execute_rollback 统一函数(所有回滚点共用) @@ -34,13 +37,16 @@ # mode="auto":指定 target_stage(Stage5->3, Stage3.2->3, Stage9->1) def execute_rollback(requirement, mode="interactive", target_stage=None, reason=""): # ── 1. 读取状态 ── - # 版本模式:versions.json 该需求对象 + # 版本模式:versions.json 该需求对象(控制权威)+ context.md(执行上下文)+ version-context.md(聚合索引) # 单需求模式:context.md YAML frontmatter state = read_state(requirement) stageHistory = state["stageHistory"] # 如 ["0","1","1.1","2","2.1","3"] currentStage = state["currentStage"] # 如 "3.1" skipDecisions = state.get("skipDecisions", {}) rollbackHistory = state.get("rollbackHistory", []) + stageOutputs = context.get("stageOutputs", {}) + artifactIndex = context.get("artifactIndex", {}) + staleArtifacts = context.get("staleArtifacts", []) stage_names = load("stage-names.json")["stage_names"] STAGE_ORDER = load("stage-names.json")["stage_order"] @@ -112,10 +118,23 @@ def execute_rollback(requirement, mode="interactive", target_stage=None, reason= k_idx = STAGE_ORDER.index(float(k) IF "." IN k ELSE int(k)) IF k_idx < target_idx THEN new_skipDecisions[k] = v + # 清理目标及之后的阶段输出;物理文件不删除,但从可消费索引移入 staleArtifacts + new_stageOutputs = {} + invalidated_artifacts = [] + FOR k, output IN stageOutputs.items(): + k_idx = STAGE_ORDER.index(float(k) IF "." IN k ELSE int(k)) + IF k_idx < target_idx THEN + new_stageOutputs[k] = output + ELSE + FOR artifact IN output.get("artifacts", []): + invalidated_artifacts.append({**artifact, "invalidatedByRollback": target_stage, "invalidatedAt": current_timestamp()}) + new_artifactIndex = rebuild_from_stage_outputs(new_stageOutputs) + new_state = { "currentStage": target_stage, "stageHistory": new_stageHistory, "skipDecisions": new_skipDecisions, + "lastCompletedStage": new_stageHistory[-1] IF new_stageHistory ELSE "", "rollbackHistory": rollbackHistory + [{ "from": currentStage, "to": target_stage, @@ -123,11 +142,33 @@ def execute_rollback(requirement, mode="interactive", target_stage=None, reason= "time": current_timestamp() }] } - # 原子写:写临时文件 -> rename 替换(杜绝写一半损坏) - atomic_write(state_file, new_state) + # 两阶段回滚checkpoint:先准备需求上下文,再提交权威控制面,最后刷新镜像/聚合索引 + rollback_checkpoint_id = "{versionName}:{reqPrefix}:rollback:{target_stage}:{ISO时间}" + atomic_write(context.md, { + checkpointId: rollback_checkpoint_id, + checkpointState: "pending", + stageOutputs: new_stageOutputs, + artifactIndex: new_artifactIndex, + staleArtifacts: staleArtifacts + invalidated_artifacts, + openRisks: append_once(openRisks, "回滚后远程副作用未补偿,请在相关阶段复核") + }) + atomic_write(versions.json该需求, {**new_state, checkpointId: rollback_checkpoint_id, contextRevision: contextRevision + 1}) + atomic_write(context.md, { + **new_state, + checkpointId: rollback_checkpoint_id, + checkpointState: "clean", + contextRevision: versions.contextRevision + }) + atomic_write(version-context.md.requirementContextIndex[reqPrefix], { + currentStage: target_stage, + lastCompletedStage: new_state.lastCompletedStage, + contextRevision: versions.contextRevision, + checkpointId: rollback_checkpoint_id, + status: "rolled_back" + }) OUTPUT f"✅ 已回退到【{stage_names[target_stage]}】(stage {target_stage}),将从该阶段重新执行" - OUTPUT f" 已清空 stage {target_stage} 及之后的 stageHistory/skipDecisions,保留之前产物" + OUTPUT f" 已清空 stage {target_stage} 及之后的控制状态/阶段输出;物理产物已标记 stale,默认不再注入" # ── 5. 继续流程:按 currentStage=target_stage 重新推进 ── # FOR 循环从 target_stage 开始(跳过 target 之前 stageHistory 仍记录已完成的) @@ -147,7 +188,7 @@ def atomic_write(state_file, new_state): os.rename(tmp_file, state_file) # rename 原子替换 ``` -**一次性写入的字段**:currentStage + stageHistory + skipDecisions + rollbackHistory,四者同一次原子写。 +**控制面一次性写入字段**:currentStage + lastCompletedStage + stageHistory + skipDecisions + rollbackHistory + checkpointId + contextRevision。需求上下文必须同步清理 stageOutputs/artifactIndex,并更新 staleArtifacts;版本模式还必须刷新 version-context.md 索引。 ## 清理规则(统一,修复 Stage5 遗漏) @@ -156,55 +197,63 @@ def atomic_write(state_file, new_state): - `stageHistory`:保留 X 之前的项,清空 X 及之后 - `skipDecisions`:清空 X 及之后的条目(让用户重新决定可跳过环节,避免回滚后该重跑的 stage 被旧决策跳过) - `rollbackHistory`:追加 `{from, to, reason, time}` +- `stageOutputs`:保留 X 之前,清空 X 及之后 +- `artifactIndex`:从保留的 stageOutputs 重建;X 及之后的物理产物移入 `staleArtifacts`,默认不注入 +- `version-context.md`:刷新该需求 currentStage/lastCompletedStage/contextRevision/checkpointId **此规则统一推广到所有回滚点**,替换原有零散实现: - Stage5 部署失败(stage-hooks.md 原仅 `重置currentStage="3"`,未清 stageHistory)-> 改调 `execute_rollback(auto, target=3)`,遗漏修复 - Stage3.2 返回修复(原手写清 stageHistory)-> 改调 `execute_rollback(auto, target=3)` -- Stage9 循环(原手写清 stageHistory+skipDecisions)-> 改调 `execute_rollback(auto, target=1)`,自动语义+MAX_CYCLES 不变 +- Stage9 循环(原手写清 stageHistory+skipDecisions)-> `loop_decision.mode` 决策后调用 `execute_rollback(auto, target=1)`;回滚目标和 MAX_CYCLES 不变 ## 恢复遍历规则 回滚到 stage X 后,继续 dev-flow 时: 1. FOR 循环**从 X 开始**(不重跑 X 之前已完成的 stage) 2. 跳过 stageHistory 中仍记录已完成的早期 stage -3. prompt 注入【已有产物路径】(沿用"继续版本开发"`dev-flow.md:1023-1026` 规则),Agent 基于已有产物 Edit 覆盖而非重建 +3. prompt 仅注入 artifactIndex 中仍有效的【已有产物路径】;staleArtifacts 默认不注入,目标阶段需要覆盖旧文件时仅传路径并明确标记“待重建” 4. 可跳过环节因 skipDecisions 已清空 X 及之后,会重新询问用户(不沿用旧跳过决策) ## 防死循环 - `rollbackHistory` 记录每次回滚 - 检测:最近 5 次回滚中,回滚到同一 stage ≥3 次 -> AskUserQuestion 提示"可能反复失败,是否人工介入" -- Stage9 自动循环另有 `MAX_CYCLES=10` 防护(stage-hooks.md:610-614),不受影响 +- Stage9 修复循环另有 `MAX_CYCLES=10` 防护;`auto` 与用户确认继续两条路径共同计数,选择暂停不消耗循环次数 ## 状态持久化位置 | 模式 | 状态文件 | 字段位置 | |------|---------|---------| -| 版本模式 | `dev/versions/versions.json` | 该需求对象内 `currentStage`/`stageHistory`/`skipDecisions`/`rollbackHistory` | +| 版本模式 | `versions.json` + 需求 `context.md` + `version-context.md` | 控制权威 + 执行上下文 + 版本聚合索引 | | 单需求模式 | `dev/active/{task}/context.md` | YAML frontmatter 同名字段 | -## 与 Stage9 自动循环的关系(保留不变) +## 与 Stage9 可控循环决策的关系 -Stage9 是 DevOps 自动循环修 bug 机制,**不是用户回退选择项**: -- 触发:测试失败自动触发(读测试报告,非用户选) -- 目标:强制 stage 1(不让用户选) +Stage9 是 DevOps 修 bug 循环机制,`loop_decision.mode` 控制是否先询问,但**不是用户回退目标选择项**: +- 触发:测试报告存在失败或缺陷 +- `mode=auto`:直接进入修复循环 +- `mode=ask`:通过 AskUserQuestion 让用户选择“进入下一轮修复”或“暂停循环,保留现场” +- 暂停:currentStage 保持 9,lastCompletedStage/stageHistory 不追加 Stage 9,恢复后重新读取报告并询问 +- 目标:进入循环后一律强制 stage 1(不让用户选择其他目标) - 附加动作:调用 req-fix-bug-analyzer 生成 bug fix 子需求 - 防死循环:MAX_CYCLES=10 -Stage9 改走 `execute_rollback(auto, target=1)` 仅统一写入逻辑,**自动语义、强制 stage1、生成 bug fix 子需求、MAX_CYCLES 全部保留不变**。用户手动回滚(interactive)与 Stage9 自动循环(auto)并存,互不干扰。 +只有在策略为 auto 或用户确认继续后,Stage9 才调用 `execute_rollback(auto, target=1)`。循环入口是否自动可配置,但**固定 stage1、生成 bug fix 子需求、MAX_CYCLES** 保持不变;选择暂停时不得调用回滚函数。 --- ## ⛔ 完成验证(回复结束前必须逐项确认) - [ ] 已 Read stage-names.json 获取中文映射 = ? +- [ ] 已 Read dev-flow-context-contract.json = ? - [ ] 回滚入口识别 = ? (Stage5 / Stage3.2 / Stage9;主菜单+失败决策入口已屏蔽) -- [ ] 模式 = ? (auto;interactive 入口已屏蔽) +- [ ] Stage9 loop_decision.mode = ? (auto / ask;缺失或非法时安全回退规则已执行) +- [ ] 模式 = ? (进入循环后为auto;interactive 回滚目标入口已屏蔽) - [ ] interactive 模式:已展示中文列表 + 默认上一步 = ?(入口已屏蔽,仅定义保留) - [ ] 用户选择目标 stage = ?(auto 模式由 target_stage 指定) - [ ] 防死循环检查 = ? (通过 / 触发提示) -- [ ] 原子写入完成 = ? (currentStage + stageHistory + skipDecisions + rollbackHistory 一次性) -- [ ] 清理规则 = ? (保留 target 之前,清空 target 及之后) +- [ ] 回滚checkpoint完成 = ? (context prepare → versions commit → context/version-context finalize) +- [ ] 清理规则 = ? (控制状态+stageOutputs保留 target 之前,后续产物移入 staleArtifacts) - [ ] 已输出"回退到【中文名】"提示 = ? - [ ] 已从 target stage 恢复 dev-flow = ? diff --git a/.claude/config/dev-flow-checklists/stage-10-version-regression.md b/.claude/config/dev-flow-checklists/stage-10-version-regression.md index 0f7f356c12e77434a3e4aed7dd3df20cd30a6e7e..83233479fe23be419ee0b88605c0eecef63d251a 100644 --- a/.claude/config/dev-flow-checklists/stage-10-version-regression.md +++ b/.claude/config/dev-flow-checklists/stage-10-version-regression.md @@ -9,15 +9,26 @@ - 版本下所有需求 Stage 9 已通过(test_passed) - versions.json 该版本 `regressionTestSetId` 非空(A5.1 已执行) - `docs/project-knowledge/module-index.json` 存在且 config_hash 一致 +- `dev/versions/{versionName}/version-context.md` 已与各需求 context.md 对账完成(contract 1.0) ## 步骤清单(严格按顺序执行) ### 10.1 前置检查 ```python +# 检查0:加载版本聚合上下文 +Read ".claude/config/dev-flow-context-contract.json" +version_context_path = f"dev/versions/{versionName}/version-context.md" +IF NOT 文件存在 version_context_path THEN + 从 versions.json + 各需求 context.md 重建 version-context.md(不得只靠主会话记忆) +END IF + # 检查1:所有需求 Stage 9 通过 FOR each req IN 版本配置.requirements: - context_path = f"dev/versions/{versionId}/completed/{req.task_name}/context.md" + context_path = version_context.requirementContextIndex[req.reqPrefix].contextPath + IF context_path 不存在 THEN + context_path = active/{req.task_name}/context.md 或 completed/{req.task_name}/context.md 中实际存在者 + END IF IF NOT 文件存在 THEN OUTPUT: "❌ 需求 {req.task_name} 未完成,无法进入 Stage 10" mcp__biz-sync__save_pending_item( @@ -27,7 +38,7 @@ FOR each req IN 版本配置.requirements: context_description=f"需求 {req.task_name} 未完成" ) GOTO 主菜单 - IF context.currentStage != 9 OR context.testResult != "test_passed" THEN + IF context.lastCompletedStage != 9 OR context.testResult != "test_passed" THEN OUTPUT: "❌ 需求 {req.task_name} Stage 9 未通过" GOTO 主菜单 @@ -63,10 +74,13 @@ IF NOT 文件存在 "docs/project-knowledge/module-index.json" THEN ### 10.2 模块选择(含"全量回归"选项) ```python -# 收集涉及模块(从各需求 context.md moduleId) +# 收集涉及模块(优先从 version-context requirementContextIndex 定位各需求 context.md) involved_modules = {} FOR each req IN 版本配置.requirements: - context_path = f"dev/versions/{versionId}/completed/{req.task_name}/context.md" + context_path = version_context.requirementContextIndex[req.reqPrefix].contextPath + IF context_path 不存在 THEN + context_path = active/{req.task_name}/context.md 或 completed/{req.task_name}/context.md 中实际存在者 + END IF IF 文件存在 THEN 读取 context_path 的 YAML frontmatter moduleId = frontmatter.get("moduleId") @@ -415,7 +429,22 @@ Skill(test-report, args=f"--requirements_dir docs/{versionName}/requirements/ -- # 内容:模块列表、用例数、通过/失败数、循环次数、bug fix 子需求列表 # 标记 stage10Completed -更新 versions.json 该版本 stage10Completed = True +stage10_result = { + "stage": "10", + "status": "completed", + "summary": "版本级回归结果摘要(<=1000字)", + "artifacts": [实际存在的版本级回归报告/事件文件路径], + "decisions": [模块选择策略, 失败处理决策], + "risks": [未关闭风险], + "context_updates": {"versionRegressionResult": event_type} +} +校验 stage10_result(复用 dev-flow-context-contract.json stage_result_schema;版本级扩展字段只写 version-context) + +# 版本级两阶段checkpoint:先保存结果,再提交versions控制面,最后finalize +stage10_checkpoint_id = "{versionName}:VERSION:10:{ISO时间}" +原子写 version-context.md: checkpointId=stage10_checkpoint_id, checkpointState="pending", stage10Result=stage10_result +原子写 versions.json 该版本: stage10Completed=True, stage10CheckpointId=stage10_checkpoint_id, contextRevision+=1 +原子写 version-context.md: checkpointState="clean", versionStatus="regression_completed", contextRevision=versions.contextRevision, updatedAt=now ``` --- @@ -441,5 +470,7 @@ Skill(test-report, args=f"--requirements_dir docs/{versionName}/requirements/ -- - [ ] 10.6 循环次数 = ?(0=一次通过) - [ ] 10.7 报告生成 = ? - [ ] versions.json stage10Completed = True +- [ ] version-context.md stage10Result/checkpointState = ?(completed/clean) +- [ ] Stage 10 输入均来自版本聚合上下文与实际产物,不依赖主会话旧缓存 = ? ⛔ 所有项确认后 → 输出"✅ Stage 10 版本级回归测试完成" → 进入 complete-version diff --git a/.claude/config/dev-flow-checklists/stage-hooks.md b/.claude/config/dev-flow-checklists/stage-hooks.md index 066ac71a867f9e5f20bf19cd7c7d463ba2c2e732..d445ddff16333be2d5dbdba7ea218c2ab4361b60 100644 --- a/.claude/config/dev-flow-checklists/stage-hooks.md +++ b/.claude/config/dev-flow-checklists/stage-hooks.md @@ -1,15 +1,124 @@ -# 18阶段推进与后置动作指引 +# 21阶段推进与后置动作指引 -> 来源:dev-flow.md "17阶段有序推进规则" + "逐阶段执行流程" + "阶段Prompt构造规则" + "阶段后置动作映射表" + "阶段后置动作执行指引"章节 +> 来源:dev-flow.md "21阶段有序推进规则" + "逐阶段执行流程" + "阶段Prompt构造规则" + "阶段后置动作映射表" + "阶段后置动作执行指引"章节 > v4.9 G4 新增:Stage 3.5 插件开发迭代评估循环(仅 dev_target=Agent/Skill/Command 触发) +> v4.9 上下文优化:统一版本/需求上下文契约、阶段结果协议与两阶段 checkpoint > 用途:执行版本模式逐阶段推进时必须Read本文件,按步骤逐项执行 -## 18阶段有序推进规则 +## 21阶段有序推进规则 ``` STAGE_ORDER = [0, 1, 1.1, 1.2, 1.6, 2, 2.1, 2.2, 3, 3.1, 3.2, 3.5, 4, 5, 5.5, 6, 6.1, 7, 8, 9, 10] +REQUIREMENT_STAGE_ORDER = STAGE_ORDER[0:-1] # Stage 10 是版本级阶段,不进入单需求 FOR 循环 ``` +## 上下文契约(P0) + +每次进入本清单必须先 Read `.claude/config/dev-flow-context-contract.json`,并验证: + +1. contract version 与 context.md 的 `contextSchemaVersion` 一致;旧 context 缺字段时先原地迁移到 1.0,保留未知字段。 +2. `versions.json` 是生命周期、currentStage、stageHistory、skipDecisions、rollbackHistory 的权威源。 +3. 需求 `context.md` 保存 stageOutputs、artifactIndex、openRisks、decisionLog 等执行上下文。 +4. `version-context.md` 保存版本快照与各需求上下文索引,供 Stage 10/complete-version 聚合消费。 +5. Agent/Skill 只能通过 stage result 的 `context_updates` 建议更新白名单字段,禁止修改 contract 中 `controller_only_fields`。 + +**来源刷新顺序**:versions.json → context.md → version-context.md → docs 产物扫描 → project-context.json。除不可变身份外,禁止跨 stage 使用主会话旧缓存。 + +### 统一阶段提交函数 `commit_stage_transition`(P0) + +所有完成、跳过、自动跳过、降级成功分支必须调用本函数;任何 `CONTINUE/BREAK/GOTO` 若代表当前 stage 已结束,必须先提交,禁止绕过: + +```python +FUNCTION commit_stage_transition(stage_result): + # 0. 校验/归一化 + VALIDATE stage_result AGAINST contract.stage_result_schema + IF Agent/Skill 未返回结构化结果 THEN + 主会话从返回文本与实际产物扫描合成 stage_result + END IF + DROP stage_result.context_updates 中不在 allowed_context_updates 的字段 + checkpoint_id = "{versionName}:{reqPrefix}:{stage}:{ISO时间}" + + # 1. prepare:先保存不可丢失的阶段结果,但不推进控制面 + 原子写 context.md(临时文件 + rename): + checkpointId = checkpoint_id + checkpointState = "pending" + stageOutputs[stage] = {status,summary,artifacts,decisions,risks,contextUpdates,completedAt} + artifactIndex = merge_and_dedupe(artifactIndex, stage_result.artifacts) + decisionLog = append_dedupe(decisionLog, stage_result.decisions) + openRisks = reconcile(openRisks, stage_result.risks) + updatedAt = now + + # 2. commit:推进权威控制面 + 原子写 versions.json(临时文件 + rename): + currentStage = stage + lastCompletedStage = stage + stageHistory = append_once(stageHistory, stage) + contextPath = context_path + contextRevision = context.contextRevision + 1 + checkpointId = checkpoint_id + # skipped/auto_skipped 也必须 append_once(stageHistory, stage),并保留 skipDecisions + + # 3. finalize:完成镜像与版本聚合 + 原子写 context.md: + lastCompletedStage = stage + stageHistory = versions.json.stageHistory + skipDecisions = versions.json.skipDecisions + rollbackHistory = versions.json.rollbackHistory + contextRevision = versions.json.contextRevision + checkpointState = "clean" + 原子写 version-context.md: + requirementContextIndex[reqPrefix] = {taskName,contextPath,currentStage,lastCompletedStage,contextRevision,status,checkpointId} + contextRevision += 1 + updatedAt = now +END FUNCTION +``` + +### Stage 9 循环策略解析与暂停持久化(P0) + +`step_mode.non_skippable["9"]` 只控制进入 Stage 9 前是否确认;测试失败后是否自动进入修复循环必须使用独立的 `loop_decision.mode`: + +```python +FUNCTION resolve_loop_decision_mode(): + config = LOAD_JSON_IF_EXISTS(".claude/config/dev-flow-interaction-config.json") + IF config != null AND config.loop_decision.mode IN ["auto", "ask"] THEN + RETURN config.loop_decision.mode + END IF + IF config != null AND config.step_mode.non_skippable["9"] == "auto_execute" THEN + RETURN "auto" # schema 1.0 兼容:保留旧配置的显式自动倾向 + END IF + RETURN "ask" # 无配置、解析失败、非法值均安全回退为询问 +END FUNCTION + +FUNCTION persist_stage_pause(stage_result): + VALIDATE stage_result AGAINST contract.stage_result_schema + # 只保存阻断现场,不将 Stage 9 标记为完成,也不推进 Stage 10 + 原子写 context.md: + stageOutputs["9"] = {status:"blocked",summary,artifacts,decisions,risks,contextUpdates,completedAt} + decisionLog = append_dedupe(decisionLog, stage_result.decisions) + openRisks = reconcile(openRisks, stage_result.risks) + APPLY stage_result.context_updates 中通过白名单校验的字段 + loopDecisionMode = "ask" + currentStage = "9" + checkpointState = "clean" + updatedAt = now + 原子写 versions.json: + currentStage = "9" + # lastCompletedStage/stageHistory 保持 Stage 8 及之前的值,不 append Stage 9 + 原子写 version-context.md: + requirementContextIndex[reqPrefix].currentStage = "9" + requirementContextIndex[reqPrefix].status = "paused" + requirementContextIndex[reqPrefix].lastCompletedStage = versions.json.lastCompletedStage + # resume 后重新读取测试报告和 loop_decision.mode,再次进入 Stage 9 循环决策 +END FUNCTION +``` + +**pending 恢复规则**: + +- context.checkpointId == versions.checkpointId:说明步骤2已提交,补执行 finalize。 +- context 为 pending 且 versions.checkpointId 不同:权威控制面未提交,不推进阶段;保留 stageOutputs 候选结果,重新执行当前 stage 或由用户确认后重提交流转。 +- versions 已推进但 context 缺 checkpoint:从 versions.json + 实际产物重建最小 stageOutputs,标记 `summary="recovered_from_artifacts"`,再 finalize。 +- 禁止继续使用“仅比较 stageHistory 长度,较长者覆盖另一处”的旧规则。 + **阶段Agent映射表**: | 阶段 | 名称 | Agent/Skill | 版本模式可跳过 | BIZ API stage | progress | @@ -120,6 +229,8 @@ Step 3: 确定执行顺序和完成判定 **⚠️ Stage 4 版本模式不可跳过**:git-commit+git-push是代码入库必须环节,版本模式下必须执行。 +**⛔ Claude Code 仓库边界(P0)**:Stage 4 提交前必须执行 `git status --short` 并显式排除根目录 `.agents/`。禁止使用会把未跟踪文件整体纳入的 `git add .`/`git add -A`;只允许按本需求实际变更路径逐项 `git add -- `。若 `.agents/` 已被意外暂存,必须先从暂存区移除再提交,且不得修改其内容。 + ## 逐阶段执行流程 对每个需求,按STAGE_ORDER顺序逐阶段执行: @@ -129,8 +240,16 @@ cycle_count = 0 MAX_CYCLES = 10 WHILE cycle_count < MAX_CYCLES: - FOR each stage IN STAGE_ORDER: + FOR each stage IN REQUIREMENT_STAGE_ORDER: stageExecutionSkipped = false # 仅用于Stage 1.1/2.1:跳过检视Agent但仍执行后置Hook + 0. 构造 stage_context(每阶段实时刷新,首次启动与resume共用): + - 读取 versions.json 当前版本与需求对象 + - 读取 context.md YAML frontmatter + - 读取 version-context.md 版本快照与 requirementContextIndex + - 扫描 docs/{versionName}/ 下属于当前 reqPrefix/任务名的已存在产物 + - 按 dev-flow-context-contract.json 的 stage_contracts[{stage}].consumes 选取字段 + - 校验 immutable_identity_fields;任一冲突立即中止,不得静默覆盖 + - 若 context.md.checkpointState == "pending",先执行下方 checkpoint 恢复规则,完成前禁止启动新阶段 1. 状态持久化(双写一致)⭐v4.9:将 currentStage={stage} **同时写入两处**,保持原子同步: - versions.json 该需求的 currentStage 字段(恢复读取的权威源) - context.md YAML frontmatter 的 currentStage 字段(保持与 versions.json 一致) @@ -149,6 +268,7 @@ WHILE cycle_count < MAX_CYCLES: END IF ``` **versions.json 写回方式**:更新该需求对象的 currentStage 字段,保留其他字段不变。 + **version-context.md 写回方式**:刷新 requirementContextIndex.{reqPrefix}.currentStage/contextRevision/status="running"。 2. 🛡️ BIZ API同步(版本模式): biz_api_url = 从versions.json该需求的biz_api_url字段读取;若为空则从环境变量BIZ_API_BASE获取 biz_task_id = 从versions.json该需求的biz_task_id字段读取 @@ -176,6 +296,7 @@ WHILE cycle_count < MAX_CYCLES: stageExecutionSkipped = true 输出 "ℹ️ Stage {stage}按项目级配置跳过检视,仅跳过检视Agent和本地优化交互;继续执行该阶段后置Hook" ELSE + commit_stage_transition({stage,status:"skipped",summary:"按项目级配置跳过",artifacts:[],decisions:["skipped_by_config"],risks:[],context_updates:{}}) CONTINUE(推进到下一阶段) END IF ELIF stage_decision == "execute" THEN @@ -192,6 +313,7 @@ WHILE cycle_count < MAX_CYCLES: stageExecutionSkipped = true 输出 "ℹ️ 用户选择跳过Stage {stage}检视,仅跳过检视Agent和本地优化交互;继续执行该阶段后置Hook" ELSE + commit_stage_transition({stage,status:"skipped",summary:"用户选择跳过",artifacts:[],decisions:["skipped"],risks:[],context_updates:{}}) CONTINUE(推进到下一阶段) END IF ELSE @@ -245,6 +367,7 @@ WHILE cycle_count < MAX_CYCLES: OUTPUT: "ℹ️ 当前项目技术栈({techStack})暂不支持代码部署前自检(仅支持 Java/Kotlin/Python/Go),自动跳过 Stage 3.2" 记录 skipDecisions["3.2"] = "auto_skipped_unsupported_tech" 到 versions.json 🛡️ 调用 safe_biz_sync(content="Stage 3.2 自动跳过(技术栈不支持)", stage="development", progress=58) + commit_stage_transition({stage:"3.2",status:"skipped",summary:"技术栈不支持代码部署前自检",artifacts:[],decisions:["auto_skipped_unsupported_tech"],risks:[],context_updates:{}}) CONTINUE(推进到下一阶段) END IF @@ -272,6 +395,7 @@ WHILE cycle_count < MAX_CYCLES: OUTPUT: "ℹ️ 当前需求 dev_target={dev_target},不触发 Stage 3.5 插件开发迭代评估循环,自动跳过" 记录 skipDecisions["3.5"] = "auto_skipped_non_plugin_target" 到 versions.json 🛡️ 调用 safe_biz_sync(content="Stage 3.5 自动跳过(非插件开发需求)", stage="development", progress=60) + commit_stage_transition({stage:"3.5",status:"skipped",summary:"非插件开发需求",artifacts:[],decisions:["auto_skipped_non_plugin_target"],risks:[],context_updates:{}}) CONTINUE(推进到下一阶段 Stage 4) END IF @@ -449,6 +573,14 @@ WHILE cycle_count < MAX_CYCLES: END IF END IF 7. 等待Subagent完成(stageExecutionSkipped == true 时无Subagent可等待) + 7.1 归一化阶段结果(所有 Agent/Skill/主会话内联阶段): + - 优先解析返回文本中的 `dev_flow_stage_result` JSON 代码块 + - 校验 stage/status/summary/artifacts/decisions/risks/context_updates + - stage 与当前阶段不一致 → 标记 failed,禁止推进 + - artifacts 中每个路径必须实际存在;不存在的路径移入 risks,不写 artifactIndex + - summary 超过1000字时由主会话压缩,保留结论、变更和未决风险 + - 无结构化结果时,主会话从返回文本、git diff 与 docs 扫描合成,不因旧 Agent 不支持协议而中断 + - controller_only_fields 出现在 context_updates 时丢弃并输出警告 7.3 ⚠️ 功能属性提取(仅Stage 0): IF stage == 0 THEN 从subagent返回的澄清结果中提取function_attributes和frontend_type @@ -607,12 +739,14 @@ WHILE cycle_count < MAX_CYCLES: IF JSON 解析失败 THEN OUTPUT: "⚠️ Stage 3.2 {subagent_type_3_2} Agent 返回无法解析为 JSON,按容错处理:默认不阻断部署" 记录 versions.json[currentVersion].codeReviewResult = { status: "json_parse_failed", agent: subagent_type_3_2, completedAt: "" } + commit_stage_transition({stage:"3.2",status:"completed",summary:"自检结果JSON解析失败,按兼容策略不阻断部署",artifacts:[],decisions:["proceed_on_parse_failure"],risks:["code_review_result_unparsed"],context_updates:{codeReviewResult:{status:"json_parse_failed",agent:subagent_type_3_2}}}) CONTINUE(推进到 Stage 4) END IF IF review_status IN ("skipped_non_java", "skipped_non_python", "skipped_non_go") THEN OUTPUT: "ℹ️ Stage 3.2 已由 {subagent_type_3_2} Agent 内部技术栈守卫跳过" 记录 versions.json[currentVersion].codeReviewResult = JSON返回值 + commit_stage_transition({stage:"3.2",status:"skipped",summary:"代码自检Agent技术栈守卫跳过",artifacts:[],decisions:[review_status],risks:[],context_updates:{codeReviewResult:JSON返回值}}) CONTINUE(推进到 Stage 4) END IF @@ -632,11 +766,12 @@ WHILE cycle_count < MAX_CYCLES: execute_rollback(mode="auto", target_stage="3", reason="Stage3.2代码自检阻断") BREAK FOR END IF - IF "强制进入部署" → 记录 versions.json[currentVersion].codeReviewResult = { ...JSON返回值, agent: subagent_type_3_2, override: "force_proceed_with_risk", overrideAt: "" } → CONTINUE + IF "强制进入部署" → 记录 versions.json[currentVersion].codeReviewResult = { ...JSON返回值, agent: subagent_type_3_2, override: "force_proceed_with_risk", overrideAt: "" } → commit_stage_transition({stage:"3.2",status:"completed",summary:"用户确认带风险进入部署",artifacts:[],decisions:["force_proceed_with_risk"],risks:["code_review_block_overridden"],context_updates:{codeReviewResult:JSON返回值}}) → CONTINUE IF "中止流程" → 更新currentStage="3.2" → EXIT WHILE ELSE 记录 versions.json[currentVersion].codeReviewResult = { ...JSON返回值, agent: subagent_type_3_2 } OUTPUT: "✅ Stage 3.2 自检通过({subagent_type_3_2}):auto_fixed={auto_fixed}, manual_items={manual_items}, changed_files={len(changed_files)}" + commit_stage_transition({stage:"3.2",status:"completed",summary:"代码部署前自检通过",artifacts:changed_files,decisions:[],risks:manual_items,context_updates:{codeReviewResult:JSON返回值}}) CONTINUE(推进到 Stage 4) END IF END IF @@ -644,21 +779,30 @@ WHILE cycle_count < MAX_CYCLES: IF stage == 3.5 THEN 调用 /plugin-dev-eval-loop 命令(参数由步骤4前置步骤2构造) 从命令返回结果解析 exit_code 与 eval_summary: - - exit_code=0(达标)→ 记录 versions.json[currentVersion].pluginEvalResult = { status: "passed", ...eval_summary } → OUTPUT: "✅ Stage 3.5 评估达标(pass_rate={pass_rate}),Golden Fixture 已沉淀" → CONTINUE(推进到 Stage 4) - - exit_code=1(未达标/迭代超限)→ AskUserQuestion("Stage 3.5 评估循环未达标(max_iterations={max_iterations} 已耗尽),如何处理?", 选项=["降级到 Stage 4(推荐)", "中止流程人工介入"]) → IF "降级" → 记录 pluginEvalResult = { status: "fallback_max_iter", ... } → CONTINUE;IF "中止" → 更新currentStage="3.5" → EXIT WHILE - - exit_code=2(评估引擎不可用/数据集不存在)→ OUTPUT: "⚠️ Stage 3.5 评估引擎不可用,自动降级到 Stage 4" → 记录 pluginEvalResult = { status: "engine_unavailable", ... } → CONTINUE + - exit_code=0(达标)→ 记录 versions.json[currentVersion].pluginEvalResult = { status: "passed", ...eval_summary } → commit_stage_transition({stage:"3.5",status:"completed",summary:"插件迭代评估达标",artifacts:golden_fixture_paths,decisions:[],risks:[],context_updates:{pluginEvalResult:eval_summary}}) → OUTPUT: "✅ Stage 3.5 评估达标(pass_rate={pass_rate}),Golden Fixture 已沉淀" → CONTINUE(推进到 Stage 4) + - exit_code=1(未达标/迭代超限)→ AskUserQuestion("Stage 3.5 评估循环未达标(max_iterations={max_iterations} 已耗尽),如何处理?", 选项=["降级到 Stage 4(推荐)", "中止流程人工介入"]) → IF "降级" → 记录 pluginEvalResult = { status: "fallback_max_iter", ... } → commit_stage_transition({stage:"3.5",status:"completed",summary:"评估未达标,用户确认降级继续",artifacts:[],decisions:["fallback_max_iter"],risks:["plugin_eval_not_passed"],context_updates:{pluginEvalResult:eval_summary}}) → CONTINUE;IF "中止" → 更新currentStage="3.5" → EXIT WHILE + - exit_code=2(评估引擎不可用/数据集不存在)→ OUTPUT: "⚠️ Stage 3.5 评估引擎不可用,自动降级到 Stage 4" → 记录 pluginEvalResult = { status: "engine_unavailable", ... } → commit_stage_transition({stage:"3.5",status:"completed",summary:"评估引擎不可用,按规则降级继续",artifacts:[],decisions:["engine_unavailable_fallback"],risks:["plugin_eval_engine_unavailable"],context_updates:{pluginEvalResult:eval_summary}}) → CONTINUE - exit_code=3(参数错误)→ OUTPUT: "❌ Stage 3.5 参数错误:{error_msg}" → 更新currentStage="3.5" → EXIT WHILE END IF END IF 9. 🛡️ BIZ API同步(版本模式):调用 mcp__biz-sync__biz_sync_session(biz_api_url=biz_api_url, biz_task_id=biz_task_id, content="{stage_name}完成", stage={stage_name}, progress={progress}) ⚠️ 使用safe_biz_sync包装器(失败时自动记录save_pending_item,不打扰用户) - 10. 将 stage 追加到 versions.json 该需求的 stageHistory 数组 - 11. 推进到 STAGE_ORDER 下一项 + 10. IF stage == 9 THEN + stage9_candidate_result = stage_result + # Stage 9 的完成提交延迟到下方循环决策;此处不得 append stageHistory + BREAK FOR + ELSE + 调用 `commit_stage_transition(stage_result)`,统一更新 versions.json/context.md/version-context.md;禁止直接单写 stageHistory + END IF + 11. 推进到 REQUIREMENT_STAGE_ORDER 下一项 END FOR - # Stage 9循环决策(FOR循环结束后) + # Stage 9循环决策(FOR循环结束后;loop_decision 独立于执行模式) + loop_decision_mode = resolve_loop_decision_mode() + 更新 context.md.loopDecisionMode = loop_decision_mode 读取测试报告,判断: IF 所有测试通过且无缺陷 THEN + commit_stage_transition({stage:"9",status:"completed",summary:"所有测试通过且无缺陷",artifacts:stage9_candidate_result.artifacts,decisions:["tests_passed"],risks:[],context_updates:{cycleDecision:"proceed_to_stage_10",loopDecisionMode:loop_decision_mode}}) 🛡️ 调用 mcp__biz-sync__biz_sync_stage(biz_api_url=biz_api_url, biz_task_id=biz_task_id, stage="completed", progress=100, status="completed") ⚠️ 使用safe_biz_sync包装器(失败时自动记录save_pending_item,不打扰用户) OUTPUT: "✅ 全流程完成,退出循环" @@ -667,12 +811,38 @@ WHILE cycle_count < MAX_CYCLES: # Stage 10 完成后才进入 complete-version BREAK ELSE IF 存在失败测试用例或缺陷 THEN + failure_summary = 从测试报告提取失败用例数、缺陷数、关键失败原因和报告路径 + OUTPUT: "⚠️ Stage 9 检测到测试失败:{failure_summary}" + + IF loop_decision_mode == "ask" THEN + AskUserQuestion( + question: "Stage 9 检测到失败测试或缺陷。是否进入下一轮修复?\n{failure_summary}", + header: "循环决策", + options: [ + {label: "进入下一轮修复(推荐)", description: "生成 bug fix 子需求,回滚到 Stage 1 并重新执行开发测试流程"}, + {label: "暂停循环,保留现场", description: "停留在 Stage 9,不生成修复需求;恢复流程时再次询问"} + ] + ) + IF 用户选择 "暂停循环,保留现场" THEN + persist_stage_pause({stage:"9",status:"blocked",summary:"测试失败,用户选择暂停循环",artifacts:stage9_candidate_result.artifacts,decisions:["pause_for_manual_intervention"],risks:[failure_summary],context_updates:{cycleDecision:"manual_pause",loopDecisionMode:"ask"}}) + 🛡️ 调用 safe_biz_sync(content="Stage 9 测试失败,用户暂停修复循环", stage="testing", progress=95) + OUTPUT: "⏸️ 已暂停在 Stage 9 并保留失败现场;恢复 dev-flow 时将重新询问" + EXIT WHILE + END IF + cycle_decision = "user_confirmed_fix_loop" + ELSE + cycle_decision = "auto_fix_loop" + OUTPUT: "🔄 循环策略为 auto,将自动进入下一轮修复" + END IF + cycle_count += 1 - OUTPUT: "🔄 检测到失败,第{cycle_count}次循环" + OUTPUT: "🔄 开始第{cycle_count}次修复循环" 调用 req-fix-bug-analyzer 生成bug fix子需求 - # ⭐v4.10:改走统一回滚函数(自动语义不变:强制 stage1 + MAX_CYCLES=10 保留 + 仍生成 bug fix 子需求) + fix_requirement_summary = 提取 bug fix 子需求摘要与产物路径 + commit_stage_transition({stage:"9",status:"completed",summary:"测试失败,已决定进入第{cycle_count}次修复循环",artifacts:stage9_candidate_result.artifacts + fix_requirement_summary.artifacts,decisions:[cycle_decision],risks:[failure_summary],context_updates:{cycleDecision:cycle_decision,fixRequirementSummary:fix_requirement_summary.summary,loopDecisionMode:loop_decision_mode}}) + # 决策完成后统一回滚;auto 与用户确认继续均强制 target=1,MAX_CYCLES=10 保留 Read .claude/config/dev-flow-checklists/rollback.md - execute_rollback(mode="auto", target_stage="1", reason="Stage9测试失败循环") + execute_rollback(mode="auto", target_stage="1", reason="Stage9测试失败循环:{cycle_decision}") CONTINUE(重新进入WHILE循环) END IF END WHILE @@ -691,6 +861,8 @@ END IF ``` {需求描述} 【输入来源】:version_mode +【上下文契约】:.claude/config/dev-flow-context-contract.json(version=1.0) +【上下文修订号】:{contextRevision} 【版本ID】:{versionName} 【项目路径】:{project_path} ← v2.4 GEP自进化核心关联点(skill 调 gep_recall/record_outcome 的 project_path 取此值,project_name 取 basename) 【项目名】:{projectName} ← v2.4 项目路径 basename(= GEP project_name,作 projects/{name}/ 存储目录名) @@ -705,13 +877,24 @@ END IF 【执行模式】:{execution_mode} 【工作目录】:dev/versions/{versionName}/active/{task_name}/ 【输出目录】:docs/{versionName}/ +【版本上下文路径】:dev/versions/{versionName}/version-context.md 【文档路径变量】:{branch} = {versionName}(版本模式下所有 docs/{branch}/ 路径替换为 docs/{versionName}/,subagent 优先使用此注入值,单需求模式无此注入时回退 git branch --show-current) 【已有产物路径】:{该需求已生成的所有文档路径列表} +【上一已完成阶段】:{lastCompletedStage} +【上一阶段摘要】:{prior_stage_summary} +【当前阶段输入】:{按 stage_contracts[stage].consumes 选择并序列化的字段;缺失必填输入必须显式标记} +【已确认决策】:{与当前阶段相关的 decisionLog 子集} +【未关闭风险】:{与当前阶段相关的 openRisks 子集} 【竞品分析摘要】:{competitor_analysis_summary} 【价值收益摘要】:{value_benefit_summary} ← P3.5收集,若用户跳过P3.5,值为"(用户跳过价值收益评估)" 【context.md路径】:dev/versions/{versionName}/active/{task_name}/context.md 【功能属性】:{functionAttributes} ← 仅当stage>=1且functionAttributes非空时注入 【前端类型】:{frontendType} ← 仅当stage>=1且frontendType非空时注入 + +【阶段结果回传协议】:完成后必须返回以下 JSON 代码块;不得直接修改 currentStage/stageHistory 等控制字段。 +~~~json dev_flow_stage_result +{"stage":"{stage}","status":"completed|skipped|blocked|failed","summary":"不超过1000字","artifacts":[{"type":"artifact_type","path":"实际存在的路径"}],"decisions":[],"risks":[],"context_updates":{}} +~~~ ``` **文档落盘路径约束(P0)**: @@ -720,7 +903,11 @@ END IF - 禁止在【工作目录】下创建 requirements/design/api/testing 子目录存放交付文档 - 构造 subagent prompt 时,文档路径必须用【输出目录】+ 子目录,不得用【工作目录】+ 子目录 -**已有产物路径**:从 versions.json 该需求的 stageHistory 和 docs/ 目录扫描获得,避免重复生成已有文档。 +**已有产物路径**:以 context.md 的 artifactIndex 为主,docs/ 目录扫描为校验与补全;不得仅依据 stageHistory 推断文件存在。 + +**最小披露原则**:主会话只注入 `stage_contracts[stage].consumes` 声明的上下文字段。禁止把 versions.json、完整 context.md 或其他需求上下文整包复制给 subagent;Stage 10 仅接收版本聚合摘要和所选模块。 + +**结果兼容策略**:旧 Agent/Skill 未返回 `dev_flow_stage_result` 时,由主会话合成同结构结果后再 checkpoint;兼容不等于跳过产物存在性和身份校验。 **条件性参数**:当context.md的functionAttributes非空时,从Stage 1起注入【功能属性】和【前端类型】。Stage 0不注入(此时尚未产生)。 @@ -1444,7 +1631,7 @@ END IF |-------|:----:|:----:|------| | Stage5 部署失败 | auto | 3 | 保留原语义,修复 stageHistory 遗漏 | | Stage3.2 自检阻断"返回修复" | auto | 3 | 保留原语义 | -| Stage9 测试失败循环 | auto | 1 | 保留自动语义+MAX_CYCLES=10 | +| Stage9 测试失败循环 | policy(auto/ask) → auto | 1 | 循环入口可自动或询问;确认进入后固定回滚到1,MAX_CYCLES=10 | **核心逻辑**:修改 currentStage + 清空 target 及之后的 stageHistory/skipDecisions + 记录 rollbackHistory + 按 target 恢复 dev-flow。原子写入(临时文件+rename)。中文映射 Read `stage-names.json`。 diff --git a/.claude/config/dev-flow-checklists/stage-names.json b/.claude/config/dev-flow-checklists/stage-names.json index 9cb16740d32b6501bbd540aa46abe4cc4d655f69..2ad9b6408204d41735a8e30b3d02a87427ff825c 100644 --- a/.claude/config/dev-flow-checklists/stage-names.json +++ b/.claude/config/dev-flow-checklists/stage-names.json @@ -1,7 +1,7 @@ { - "version": "1.0", + "version": "1.1", "description": "dev-flow stage 中文映射 + STAGE_ORDER 单一真相源。回滚展示与查看版本状态复用,避免中文名散落维护。数据源对齐 stage-hooks.md 阶段Agent映射表。", - "stage_order": [0, 1, 1.1, 1.2, 1.6, 2, 2.1, 2.2, 3, 3.1, 3.2, 3.5, 4, 5, 5.5, 6, 6.1, 7, 8, 9], + "stage_order": [0, 1, 1.1, 1.2, 1.6, 2, 2.1, 2.2, 3, 3.1, 3.2, 3.5, 4, 5, 5.5, 6, 6.1, 7, 8, 9, 10], "stage_names": { "0": "需求澄清", "1": "需求分析", @@ -22,6 +22,7 @@ "6.1": "回归测试同步", "7": "测试执行", "8": "测试报告", - "9": "循环决策" + "9": "循环决策", + "10": "版本级回归测试" } } diff --git a/.claude/config/dev-flow-checklists/start-development.md b/.claude/config/dev-flow-checklists/start-development.md index 9f60535e374768ce95d3b11e0744f36d59ba369e..3a4f9188f87e77b1f3edcb3209e5fca6463fdf4f 100644 --- a/.claude/config/dev-flow-checklists/start-development.md +++ b/.claude/config/dev-flow-checklists/start-development.md @@ -223,7 +223,7 @@ END IF **强制规则**:即便检测到项目级配置已存在,也**必须**询问用户是否需要调整。本步骤在 P4 之后、P5 之前执行。 -**配置作用**:版本全流程 21 个环节(12 个不可跳过 + 9 个可跳过)的「跳过询问/自动执行」策略。每个模板同时包含 step_mode 和 fast_mode 两套配置(用户选模板时同时配置两个模式;实际运行时按 P5 选择的 execution_mode 读取对应模式配置,详见 stage-hooks.md Step 3/4.5)。 +**配置作用**:版本全流程 21 个环节(12 个不可跳过 + 9 个可跳过)的「跳过询问/自动执行」策略,以及 Stage 9 测试失败后的循环决策策略。每个模板同时包含 step_mode、fast_mode 和独立的 `loop_decision.mode`;`loop_decision` 不受 P5 执行模式影响,也不等同于 `step_mode.non_skippable.9`(后者只控制进入 Stage 9 前是否确认)。 **配置文件路径**: - 模板库(系统级,随 install 安装更新):`.claude/config/dev-flow-interaction-templates.json` @@ -233,6 +233,18 @@ END IF ``` CONFIG_PATH = ".claude/config/dev-flow-interaction-config.json" config_exists = FILE_EXISTS(CONFIG_PATH) + +FUNCTION normalize_loop_decision(config, config_exists): + IF config == null THEN config = {} + IF config.loop_decision.mode IN ["auto", "ask"] THEN RETURN config + # 兼容 schema 1.0:沿用旧配置对 Stage 9 的显式确认倾向;无配置或非法值时安全回退为 ask + IF config_exists AND config.step_mode.non_skippable["9"] == "auto_execute" THEN + config.loop_decision.mode = "auto" + ELSE + config.loop_decision.mode = "ask" + END IF + RETURN config +END FUNCTION ``` **步骤2:根据存在性分流** @@ -245,14 +257,14 @@ IF NOT config_exists THEN header: "交互配置", options: [ {label: "是,现在设置", description: "选择模板或自定义,配置各环节的跳过/自动执行策略"}, - {label: "否,使用默认", description: "沿用系统默认行为(可跳过环节逐个询问,不可跳过环节自动执行)"} + {label: "否,使用默认", description: "沿用系统默认行为(可跳过环节逐个询问、不可跳过环节自动执行、测试失败时询问是否循环)"} ] ) - IF "否,使用默认" → interaction_config = null → 写入context.md的interactionConfig字段为空 → 进入P5 + IF "否,使用默认" → interaction_config = null、loopDecisionMode = "ask" → 写入context.md的interactionConfig字段为空 → 进入P5 IF "是,现在设置" → 执行步骤3(选择模板)→ 执行步骤4(确认写入,is_new=true) ELSE # 场景2:调整设置(即便存在也强制询问) - 读取 CONFIG_PATH → 提取 template_name 和 configured_at + 读取 CONFIG_PATH → 调用 normalize_loop_decision(config, true) → 提取 template_name、configured_at 和 loop_decision.mode AskUserQuestion( question: "检测到当前项目已存在交互特性配置({template_name},配置于{configured_at})。是否需要调整?", header: "调整配置", @@ -277,7 +289,7 @@ AskUserQuestion( question: "请选择配置模板:", header: "配置模板", options: [ - {label: "标准开发模式(推荐)", description: "可跳过环节均询问;分步模式部署确认环节询问,其余自动执行"}, + {label: "标准开发模式(推荐)", description: "可跳过环节均询问;测试失败时询问是否进入下一轮修复"}, {label: "极简高效模式", description: "可跳过环节默认跳过(除3.2/3.5询问);不可跳过环节均自动执行"}, {label: "质量优先模式", description: "检视/自检环节强制执行;分步模式核心环节均询问"}, {label: "自定义模式", description: "逐环节配置(6组向导,8-10轮)"} @@ -291,10 +303,11 @@ ELSE → 从 dev-flow-interaction-templates.json 的 templates.{对应id} 读取 **步骤4:确认并写入配置(含摘要展示)** ``` -# 渲染配置摘要(含21个环节完整名称,输出step_mode和fast_mode两张Markdown表格) +# 渲染配置摘要(含21个环节完整名称,输出step_mode和fast_mode两张Markdown表格,并单列循环决策策略) OUTPUT render_config_summary(config) # 表格列:序号 | 环节名称 | 跳过支持 | 配置值 # 环节名称取自 stage-hooks.md 阶段Agent映射表(0=需求澄清 ... 10=版本级回归测试) + # 循环决策:config.loop_decision.mode(auto=自动进入修复循环;ask=每次失败询问用户) IF is_new == true THEN AskUserQuestion( @@ -329,13 +342,14 @@ END IF # 写入操作(原子化:先写临时文件再rename) 写入 CONFIG_PATH: { - "schema_version": "1.0", + "schema_version": "1.1", "template_id": {template_id 或 "custom"}, "template_name": {template_name 或 "自定义配置"}, "configured_at": {当前ISO 8601时间}, "configured_by": {当前用户}, "step_mode": {config.step_mode}, - "fast_mode": {config.fast_mode} + "fast_mode": {config.fast_mode}, + "loop_decision": {config.loop_decision} } interaction_config = 写入的config对象 OUTPUT "✅ 交互特性配置已写入:{CONFIG_PATH}(模板:{template_name})" @@ -345,7 +359,7 @@ OUTPUT "✅ 交互特性配置已写入:{CONFIG_PATH}(模板:{template_nam ``` 读取 dev-flow-interaction-templates.json 的 custom_mode.groups(6组) -config = base(若base为null则初始化空config,含空step_mode和fast_mode) +config = normalize_loop_decision(base, base != null)(若base为null则初始化空config,含空step_mode、fast_mode和loop_decision) FOR each group IN groups: FOR each stage IN group.stages: 环节名称 = 从stage-hooks.md映射表查stage对应名称 @@ -373,17 +387,29 @@ FOR each group IN groups: END IF END FOR END FOR + +# Stage 9 测试失败循环是独立决策,不复用 non_skippable["9"] +AskUserQuestion( + question: "Stage 9 检测到测试失败或缺陷后,如何决定是否进入下一轮修复?", + header: "循环决策", + options: [ + {label: "每次询问我(推荐)", description: "展示失败摘要,由我决定进入下一轮修复或暂停保留现场"}, + {label: "自动进入修复循环", description: "自动生成 bug fix 子需求并回滚到 Stage 1,最多循环 10 次"} + ] +) +config.loop_decision.mode = ("每次询问我"→"ask") / ("自动进入修复循环"→"auto") → 返回 config 对象 ``` **结果传递**: -- 配置持久化:写入 `.claude/config/dev-flow-interaction-config.json`(项目级唯一真相源;stage-hooks.md Step 3/4.5 和 standalone-mode.md 步骤2/3.5 统一从该文件读取,保证版本模式/单需求模式/恢复场景行为一致) -- 写入 context.md YAML frontmatter:`interactionConfig: {template_id 或 "custom" 或 ""(无配置时)}`(追溯标记,记录所用模板;实际配置数据在上述 json 文件中) +- 配置持久化:写入 `.claude/config/dev-flow-interaction-config.json`(项目级唯一真相源;step_mode/fast_mode 由 stage-hooks.md 与 standalone-mode.md 统一读取,`loop_decision` 由版本模式 Stage 9 读取,保证恢复场景行为一致) +- 写入 context.md YAML frontmatter:`interactionConfig: {template_id 或 "custom" 或 ""(无配置时)}` 与 `loopDecisionMode: {auto|ask}`(追溯标记;实际配置数据在上述 json 文件中)。无配置时 `loopDecisionMode=ask`。 - 步骤D版本参数块新增变量 `{interaction_config}`:P4.5加载的对象(备用缓存;stage-hooks 实际从文件读取) **容错与回退**: -- P4.5 任何交互异常/用户取消 → interaction_config = null → 回退系统默认行为,不阻塞版本启动 -- CONFIG_PATH JSON 解析失败 → interaction_config = null → 输出警告 → 进入 P5(向后兼容) +- P4.5 任何交互异常/用户取消 → interaction_config = null、loopDecisionMode=ask → 回退系统默认行为,不阻塞版本启动 +- CONFIG_PATH JSON 解析失败 → interaction_config = null、loopDecisionMode=ask → 输出警告 → 进入 P5(安全回退) +- schema 1.0 配置缺少 `loop_decision` → 按 `step_mode.non_skippable["9"]` 迁移:`auto_execute→auto`,其余值→`ask`;只在用户确认写入时升级磁盘配置,读取阶段不静默改文件 ### P5. 执行模式选择(使用 AskUserQuestion): - 选项1: "**快速模式(推荐)** — 自动执行所有阶段(需求澄清→需求分析→设计→开发→测试)" @@ -393,31 +419,53 @@ END FOR ### P6. 工作区初始化 → 创建 `dev/versions/{versionName}/active/{task_name}/` 目录(如不存在) +→ Read `.claude/config/dev-flow-context-contract.json`,校验 `version == "1.0"` +→ 读取/刷新 `dev/versions/{versionName}/version-context.md`;不存在时按 create-version.md 步骤6补建 → 创建 `context.md`,使用以下结构化格式(YAML frontmatter + Markdown正文): ```markdown --- +contextSchemaVersion: "1.0" +contextRevision: 1 +checkpointId: "" +checkpointState: "clean" # clean/pending;pending 表示阶段结果尚未完成控制面提交 currentStage: "0" +lastCompletedStage: "" stageHistory: [] skipDecisions: {} rollbackHistory: [] # ⭐v4.10 回滚历史,记录每次 {from,to,reason,time},初始空数组 taskName: "{task_name}" requirementType: "{确认后的分类类型}" executionMode: "{快速模式/分步模式}" +interactionConfig: "{template_id 或 custom 或空字符串}" +loopDecisionMode: "{normalize_loop_decision 后的 auto|ask;无配置时为 ask}" inputSource: "version_mode" reqIndex: "{2位零填充编号,如01}" reqPrefix: "REQ-{reqIndex}" versionName: "{versionName}" +versionContextPath: "dev/versions/{versionName}/version-context.md" dpmsStoryId: "{dpmsStoryId}" testSetId: "{testSetId}" +regressionTestSetId: "{regressionTestSetId}" releasePlanId: "{releasePlanId}" productId: "{productId}" productName: "{productName}" # 🆕 A3创建需求后通过get-storys回填,初始为null +subsystemId: "{subsystemId}" +subsystemName: "{subsystemName}" +subsystemVersionId: "{subsystemVersionId}" +branchName: "{branchName}" +pipelineId: "{pipelineId}" +pipelineVersion: "{pipelineVersion}" functionAttributes: "" # 🆕 需求澄清后填入,如["前端","后端"] frontendType: "" # 🆕 需求澄清后填入,如"纯前端"或"前端+后台web API" selectedDevAgents: [] # 🆕 Stage 3前置步骤1填入,如["java-code-developer","frontend-code-developer"] knowledgeBaseStatus: "" # P7填入:"built"/"skipped" moduleIndexPath: "" # P7填入:module-index.json路径(若built) +stageOutputs: {} # stage -> {status,summary,artifacts,decisions,risks,completedAt} +artifactIndex: {} # artifactType -> path[],由主会话归一化维护 +staleArtifacts: [] # 回滚失效但物理文件仍存在的产物,默认不注入后续阶段 +openRisks: [] # 尚未关闭的跨阶段风险 +decisionLog: [] # 不含控制字段的业务/技术决策 createdAt: "{ISO 8601时间}" updatedAt: "{ISO 8601时间}" --- @@ -447,7 +495,12 @@ updatedAt: "{ISO 8601时间}" {用户需求描述} ``` -⚠️ **关键**:YAML frontmatter中的`currentStage`/`stageHistory`/`skipDecisions`是逐阶段控制的状态持久化字段,每个阶段完成后必须更新。恢复时从这些字段读取,而非解析正文文本。 +⚠️ **关键**:YAML frontmatter 中的 `currentStage/stageHistory/skipDecisions` 是控制字段镜像,权威值来自 versions.json;`stageOutputs/artifactIndex/openRisks/decisionLog` 是跨阶段执行上下文。每个阶段完成或跳过后必须通过 stage-hooks.md 的统一 checkpoint 更新,禁止 Agent/Skill 直接写控制字段。 + +**P6 双层索引写回**: +1. 更新 versions.json 该需求:`contextPath/contextSchemaVersion/contextRevision/lastCompletedStage`。 +2. 更新 version-context.md 的 `requirementContextIndex.{reqPrefix}`:`taskName/contextPath/currentStage/lastCompletedStage/contextRevision/status`。 +3. 任一不可变身份字段(versionName/taskName/reqIndex/reqPrefix)在三处不一致时立即中止启动,禁止静默覆盖。 ### P7. 知识库构建(可跳过) @@ -524,7 +577,7 @@ END IF ### 前置步骤结果传递规则 -P2-P4的结果必须同时写入两个位置: +P0-P7 的结果必须先归一化,再同时写入需求上下文与阶段 Prompt 变量;禁止仅保存在主会话临时记忆中: 1. **写入context.md Markdown正文**: - P2模板适配结果:追加到context.md"需求描述"章节(适配后的模板内容替换原始描述;如未适配则保持原文) @@ -537,8 +590,11 @@ P2-P4的结果必须同时写入两个位置: - `value_benefit_summary` = P3.5价值收益评估结果摘要;若用户跳过P3.5,值为"(用户跳过价值收益评估)" - `knowledge_base_status` = P7执行结果("built"/"skipped") - `module_index_path` = "docs/project-knowledge/module-index.json"(若built)或 ""(若skipped) - - ⚠️ **层面1约束**:`knowledge_base_status`和`module_index_path`不注入任何阶段Prompt,仅持久化备用 - - 上述变量在步骤E构造阶段Prompt时分别填入`{需求描述}`、【竞品分析摘要】和【价值收益摘要】字段 + - `version_context_path` = `dev/versions/{versionName}/version-context.md` + - `context_contract_path` = `.claude/config/dev-flow-context-contract.json` + - `prior_stage_summary/artifact_index/open_risks/decision_log` = 从 context.md 对应字段实时读取,禁止使用启动时缓存 + - `knowledge_base_status/module_index_path` 仅在契约对应阶段的 `consumes` 声明需要时注入 + - 上述变量在步骤E按 context contract 的阶段白名单填入,未声明字段不得整包注入 ## 并行启动(核心实现) @@ -612,10 +668,17 @@ urllib.request.urlopen(req) - {biz_task_id}:步骤A创建的Business API Task ID - {biz_api_url}:从环境变量BIZ_API_BASE获取,默认http://localhost:50009 - {execution_mode}:用户在P5选择的执行模式 -- {interaction_config}:P4.5加载的项目级交互特性对象(含step_mode/fast_mode,或null)。⚠️ stage-hooks.md Step 3/4.5 实际从 .claude/config/dev-flow-interaction-config.json 文件读取(保证版本模式/单需求模式/恢复场景一致),此参数为备用缓存 +- {interaction_config}:P4.5加载的项目级交互特性对象(含step_mode/fast_mode/loop_decision,或null)。⚠️ stage-hooks.md Step 3/4.5 与 Stage 9 循环决策实际从 .claude/config/dev-flow-interaction-config.json 文件读取(保证恢复场景一致),此参数为备用缓存 - {competitor_analysis_summary}:P3竞品分析结果摘要(若用户跳过P3,值为"(用户跳过竞品分析)") - {value_benefit_summary}:P3.5价值收益评估结果摘要(若用户跳过P3.5,值为"(用户跳过价值收益评估)") - {context_md_path}:context.md文件路径 +- {version_context_path}:版本级上下文文件路径 +- {context_contract_path}:`.claude/config/dev-flow-context-contract.json` +- {context_revision}:context.md 当前修订号;构造 Prompt 前必须重新读取 +- {prior_stage_summary}:最近一个已完成阶段的归一化摘要(<=1000字) +- {artifact_index}:当前阶段契约允许消费的产物路径子集 +- {open_risks}:尚未关闭且与当前阶段相关的风险 +- {decision_log}:与当前阶段相关的已确认业务/技术决策 ⚠️ 不再构造独立Prompt模板。所有阶段Prompt统一使用步骤E的"阶段Prompt构造规则"。 ⚠️ BIZ API同步由主会话在逐阶段控制流程中执行(WHILE+FOR循环step 2/9),不传入subagent。 @@ -624,9 +687,11 @@ urllib.request.urlopen(req) - Stage 0(需求澄清)完成后,主会话从澄清结果提取function_attributes和frontendType,更新context.md - Stage 3前置步骤1执行后,将selected_agents写入context.md的selectedDevAgents字段 +⚠️ **禁止使用陈旧缓存**:除 immutable identity 外,步骤E 每进入一个 stage 都必须重新读取 versions.json、context.md、version-context.md 和产物索引。恢复流程与首次启动使用同一构造逻辑。 + **步骤E — 逐阶段启动 Subagent**(🚨 P0级强制 — 禁止跳过或替换): -⚠️ **架构决策**:版本模式下,主会话按STAGE_ORDER有序列表逐阶段启动subagent,**禁止**由单个subagent独立编排16阶段全流程。 +⚠️ **架构决策**:版本模式下,主会话按STAGE_ORDER有序列表逐阶段启动subagent,**禁止**由单个subagent独立编排21阶段全流程。 **原因**:subagent独立编排已被实践证明不可靠(上下文压缩导致子阶段丢失、错误恢复时主会话接管导致编排断裂)。 @@ -643,9 +708,10 @@ urllib.request.urlopen(req) - [ ] P3 竞品分析 = ? (执行/跳过) - [ ] P3.5 价值收益评估 = ? (执行/跳过) - [ ] P4 项目上下文确认 = ? (确认/生成/继续默认) -- [ ] P4.5 项目级交互配置 = ? (新增/调整/沿用/删除/跳过默认) +- [ ] P4.5 项目级交互配置 = ? (新增/调整/沿用/删除/跳过默认),loopDecisionMode = ? (auto/ask) - [ ] P5 执行模式 = ? - [ ] P6 工作区初始化 = ? +- [ ] P6 context contract/version-context/requirement context 三处身份一致 = ? - [ ] P7 知识库构建 = ? (built/skipped/部分built) - [ ] 步骤A-C: Task/Session/Version = ? - [ ] 步骤D-E: 参数块+逐阶段启动 = ? diff --git a/.claude/config/dev-flow-context-contract.json b/.claude/config/dev-flow-context-contract.json new file mode 100644 index 0000000000000000000000000000000000000000..397dec84084005baedc987a5484139fe04bb9989 --- /dev/null +++ b/.claude/config/dev-flow-context-contract.json @@ -0,0 +1,164 @@ +{ + "version": "1.0", + "updated_at": "2026-07-17", + "description": "dev-flow version 模式的版本级/需求级上下文传递契约", + "source_precedence": [ + "dev/versions/versions.json", + "dev/versions/{versionName}/{active|completed}/{taskName}/context.md", + "dev/versions/{versionName}/version-context.md", + "docs/{versionName}/ 产物扫描", + "project-context.json" + ], + "immutable_identity_fields": [ + "versionName", + "taskName", + "reqIndex", + "reqPrefix" + ], + "controller_only_fields": [ + "currentStage", + "lastCompletedStage", + "stageHistory", + "skipDecisions", + "rollbackHistory", + "contextRevision", + "checkpointId", + "checkpointState" + ], + "stage_result_schema": { + "required": [ + "stage", + "status", + "summary", + "artifacts", + "decisions", + "risks", + "context_updates" + ], + "status_values": [ + "completed", + "skipped", + "blocked", + "failed" + ], + "summary_max_chars": 1000, + "allowed_context_updates": [ + "functionAttributes", + "frontendType", + "frontendDevMode", + "selectedDevAgents", + "dev_target", + "moduleId", + "moduleName", + "testClarificationPath", + "testScope", + "environmentInfo", + "testResult", + "eventType", + "injection_risk", + "codeReviewResult", + "pluginEvalResult", + "loopDecisionMode", + "cycleDecision", + "fixRequirementSummary", + "versionRegressionResult" + ] + }, + "prompt_sections": [ + "identity", + "version_snapshot", + "requirement_snapshot", + "flow_state", + "prior_stage_summary", + "artifact_index", + "open_decisions_and_risks", + "stage_specific_inputs", + "stage_result_protocol" + ], + "stage_contracts": { + "0": { + "consumes": ["requirementDescription", "competitorAnalysisSummary", "valueBenefitSummary", "projectContextSummary"], + "produces": ["clarificationSummary", "functionAttributes", "frontendType", "dev_target"] + }, + "1": { + "consumes": ["clarificationSummary", "requirementType", "projectContextSummary"], + "produces": ["requirementDocument", "acceptanceCriteriaSummary"] + }, + "1.1": { + "consumes": ["requirementDocument", "acceptanceCriteriaSummary"], + "produces": ["requirementReviewSummary", "requirementOptimizationDecision"] + }, + "1.2": { + "consumes": ["requirementDocument", "requirementReviewSummary", "moduleIndexPath"], + "produces": ["requirementKnowledgeSyncSummary"] + }, + "1.6": { + "consumes": ["requirementDocument", "projectTechStack", "moduleIndexPath"], + "produces": ["componentDependencySummary", "componentDependencyArtifacts"] + }, + "2": { + "consumes": ["requirementDocument", "requirementReviewSummary", "componentDependencySummary", "injection_risk"], + "produces": ["designDocument", "designDecisionSummary", "frontendDevMode"] + }, + "2.1": { + "consumes": ["designDocument", "requirementDocument", "componentDependencySummary"], + "produces": ["designReviewSummary", "designOptimizationDecision", "apiArtifacts"] + }, + "2.2": { + "consumes": ["designDocument", "designReviewSummary", "moduleIndexPath"], + "produces": ["designKnowledgeSyncSummary"] + }, + "3": { + "consumes": ["requirementDocument", "designDocument", "apiArtifacts", "selectedDevAgents", "openRisks"], + "produces": ["codeChangeSummary", "changedFiles", "pluginPath"] + }, + "3.1": { + "consumes": ["codeChangeSummary", "changedFiles", "moduleIndexPath"], + "produces": ["codeKnowledgeSyncSummary"] + }, + "3.2": { + "consumes": ["codeChangeSummary", "changedFiles", "projectTechStack"], + "produces": ["codeReviewResult"] + }, + "3.5": { + "consumes": ["pluginPath", "dev_target", "codeReviewResult"], + "produces": ["pluginEvalResult", "goldenFixtureArtifacts"] + }, + "4": { + "consumes": ["changedFiles", "codeReviewResult", "pluginEvalResult", "branchName", "pipelineId"], + "produces": ["commitId", "pushResult", "deploymentTriggerSummary"] + }, + "5": { + "consumes": ["pipelineId", "pipelineVersion", "deploymentTriggerSummary"], + "produces": ["deploymentStatus", "deploymentDecision"] + }, + "5.5": { + "consumes": ["requirementDocument", "designDocument", "apiArtifacts", "codeChangeSummary", "deploymentStatus"], + "produces": ["testClarificationPath", "testScope", "environmentInfo"] + }, + "6": { + "consumes": ["testClarificationPath", "testScope", "environmentInfo", "requirementDocument", "designDocument", "apiArtifacts"], + "produces": ["testCaseArtifacts", "featureArtifacts", "moduleId", "moduleName"] + }, + "6.1": { + "consumes": ["testCaseArtifacts", "featureArtifacts", "moduleId", "moduleName"], + "produces": ["regressionSyncSummary"] + }, + "7": { + "consumes": ["testCaseArtifacts", "featureArtifacts", "codeChangeSummary", "environmentInfo"], + "produces": ["testExecutionArtifacts", "eventType", "testResult"] + }, + "8": { + "consumes": ["requirementDocument", "designDocument", "testCaseArtifacts", "testExecutionArtifacts", "testResult"], + "produces": ["testReportArtifacts", "testReportSummary"] + }, + "9": { + "consumes": ["testResult", "testReportSummary", "openRisks", "rollbackHistory", "loopDecisionMode"], + "produces": ["cycleDecision", "fixRequirementSummary"] + }, + "10": { + "consumes": ["versionRequirementSummaries", "involvedModules", "regressionTestSetId", "versionOpenRisks"], + "produces": ["versionRegressionArtifacts", "versionRegressionResult", "versionRegressionSummary"] + } + } +} diff --git a/.claude/config/dev-flow-interaction-templates.json b/.claude/config/dev-flow-interaction-templates.json index dfb8eb014bb4e95e6d2bd10f339645a45d1219ed..395bf9f7123e8230ea1c53e6ad00c8bd5c13f177 100644 --- a/.claude/config/dev-flow-interaction-templates.json +++ b/.claude/config/dev-flow-interaction-templates.json @@ -1,6 +1,6 @@ { - "schema_version": "1.0", - "last_updated": "2026-07-09", + "schema_version": "1.1", + "last_updated": "2026-07-17", "description": "版本全流程交互特性配置模板库(项目级)。由 dev-flow-checklists/start-development.md P4.5 与 standalone-mode.md 读取,供用户选择预定义模板或自定义配置。", "stage_constants": { "non_skippable_stages": ["0", "1", "2", "3", "4", "5", "5.5", "6", "7", "8", "9", "10"], @@ -17,12 +17,19 @@ "skip": "跳过该环节,不询问", "ask": "询问用户是否执行/跳过" }, - "note": "快速模式下 non_skippable 环节一律 auto_execute(快速模式定义),故 fast_mode 仅配置 skippable" + "loop_decision_values": { + "auto": "Stage 9 检测到测试失败时,自动生成修复子需求并回滚到 Stage 1", + "ask": "Stage 9 每次检测到测试失败时,询问用户是否进入下一轮修复" + }, + "note": "快速模式下 non_skippable 环节一律 auto_execute(快速模式定义),故 fast_mode 仅配置 skippable;loop_decision 独立于执行模式和 step_mode.non_skippable.9,后者只控制进入 Stage 9 前是否确认" }, "templates": { "standard_development": { "name": "标准开发模式", - "description": "可跳过环节均询问;分步模式部署确认环节询问,其余自动执行", + "description": "可跳过环节均询问;分步模式部署确认环节询问;测试失败时询问是否进入下一轮修复", + "loop_decision": { + "mode": "ask" + }, "step_mode": { "non_skippable": { "0": "auto_execute", @@ -67,6 +74,9 @@ "minimal_efficient": { "name": "极简高效模式", "description": "可跳过环节默认跳过(除 3.2/3.5 询问);不可跳过环节均自动执行", + "loop_decision": { + "mode": "auto" + }, "step_mode": { "non_skippable": { "0": "auto_execute", @@ -110,7 +120,10 @@ }, "quality_first": { "name": "质量优先模式", - "description": "检视/自检环节强制执行;分步模式核心环节均询问", + "description": "检视/自检环节强制执行;分步模式核心环节均询问;测试失败时询问是否进入下一轮修复", + "loop_decision": { + "mode": "ask" + }, "step_mode": { "non_skippable": { "0": "ask", @@ -154,7 +167,7 @@ } }, "custom_mode": { - "description": "自定义模式:逐环节向导(6 组,8-10 轮 AskUserQuestion)", + "description": "自定义模式:逐环节向导(6 组)并单独配置 Stage 9 测试失败循环决策", "groups": [ {"name": "需求阶段", "stages": ["0", "1", "1.1", "1.2", "1.6"]}, {"name": "设计阶段", "stages": ["2", "2.1", "2.2"]}, diff --git a/.gitignore b/.gitignore index a3d391ebc4fbfbac0776cd8c2cea6370e9a4f20f..3d2e9afe64b34e3a18ad0eeb3b4d3192a1900911 120000 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ NUL # Claude Code临时文件 tmpclaude-* +# 非 Claude Code 项目元数据(禁止创建/提交到远程仓库) +.agents/ + # IDE配置文件 .idea/ @@ -55,4 +58,4 @@ logs/ temp_tests/ # Bot配置(含密钥,使用 bots.yaml.example 作为模板) -**/config/bots.yaml \ No newline at end of file +**/config/bots.yaml diff --git a/output/CODE_GO_USAGE_GUIDE.md b/output/CODE_GO_USAGE_GUIDE.md index 32edea1ee13ac37bad10366c77d0139320d5813b..7a728e115cfdcdb0e5a03a127e4904afe24bdb90 100644 --- a/output/CODE_GO_USAGE_GUIDE.md +++ b/output/CODE_GO_USAGE_GUIDE.md @@ -55,7 +55,7 @@ codego-v4.8.zip | 模式 | 命令 | 用途 | |------|------|------| -| `--core` | `install.bat --core <项目路径> [--username USER]` | 将 `.claude` 注入到外部工程项目(含 MCP 配置合并) | +| `--core` | `install.bat --core <项目路径> [--username USER] [--frontend-repo-url GIT_URL]` | 将 `.claude` 注入到外部工程项目(含 MCP 配置合并及千手前端仓库配置) | | `--manage` | `install.bat --manage [部署目录] [参数...]` | 安装并启动全局管理服务(clawrelay-api/task-viewer/wecom-server) | **场景 1:为外部工程项目安装 .claude 能力** @@ -67,8 +67,12 @@ install.bat --core D:\Workspace\your-project # 指定项目管理员用户名(用于权限控制) install.bat --core D:\Workspace\your-project --username zhangsan +# 指定千手平台执行前端开发时使用的Git仓库地址 +install.bat --core D:\Workspace\your-project --frontend-repo-url "git@code.weoa.com:your-team/your-frontend.git" + # 脚本会自动完成: # - 复制 .claude 目录到目标项目(settings.json/local.json 通过 merge 保留自定义) +# - 将 --frontend-repo-url 写入 .claude/config/frontend-platform-config.json;未传时保留安装包默认值并给出提示 # - 执行 MCP 配置安装脚本(安装 db-service/sdp/shimo-mcp/tctp-dpms-set) # - 完成后即可在目标项目中使用 /dev-flow 等命令 ``` @@ -104,6 +108,9 @@ install.bat --manage D:\code-go-base --clawrelay-model claude-sonnet-4-6 --bot-i |------|:----:|------|------| | `<项目路径>` | 是 | 外部工程项目根目录 | `D:\Workspace\mide` | | `--username` | 否 | 项目管理员用户名(用于权限控制) | `zhangsan` | +| `--frontend-repo-url` | 否 | 千手平台前端开发使用的Git仓库地址;支持HTTPS、SSH和`git@host:path`格式,写入`defaults.repository_url` | `git@code.weoa.com:your-team/your-frontend.git` | + +未传 `--frontend-repo-url` 时,安装脚本不会清空仓库地址,而是保留安装包中 `frontend-platform-config.json` 的默认值,并在控制台展示当前值及重新配置示例。参数缺值或格式不合法时,脚本会展示支持格式和可直接复制的命令示例。 **部署后的目录结构**: diff --git a/output/install.bat b/output/install.bat index 4b687911bb69cc9a11f0e966b1c55907936cfb62..2b21bbe31a23b484cf4940dc2998904cb0f067eb 100644 --- a/output/install.bat +++ b/output/install.bat @@ -19,12 +19,13 @@ if /I "%~1"=="--core" goto :mode_core if /I "%~1"=="--manage" goto :mode_manage echo [Usage] -echo install.bat --core ^ [--username USER] +echo install.bat --core ^ [--username USER] [--frontend-repo-url GIT_URL] echo install.bat --manage [deploy-dir] [--clawrelay-model MODEL] [--clawrelay-port PORT] [--bot-id ID] [--bot-secret SECRET] [--wecom-model MODEL] [--master MASTER] echo. echo Scenario 1 - Install .claude to external project: echo install.bat --core D:\Workspace\mide echo install.bat --core D:\Workspace\mide --username zhangsan +echo install.bat --core D:\Workspace\mide --frontend-repo-url "git@code.weoa.com:your-team/your-frontend.git" echo. echo Scenario 2 - Install global management services (deployment-materials): echo install.bat --manage @@ -49,6 +50,8 @@ if "%TARGET_PROJECT:~-1%"=="\" set "TARGET_PROJECT=%TARGET_PROJECT:~0,-1%" REM Parse --core extra args set "ARG_USERNAME=" +set "ARG_FRONTEND_REPO_URL=" +set "USER_FRONTEND_REPO_URL=0" REM Skip --core and project path args shift @@ -57,13 +60,52 @@ shift :parse_core_args if "%~1"=="" goto :core_execute if /I "%~1"=="--username" ( + if "%~2"=="" ( + echo [ERROR] --username requires a value. + echo Example: install.bat --core D:\Workspace\mide --username zhangsan + exit /b 2 + ) set "ARG_USERNAME=%~2" + if "!ARG_USERNAME:~0,2!"=="--" ( + echo [ERROR] --username requires a value; received another option instead. + echo Example: install.bat --core D:\Workspace\mide --username zhangsan + exit /b 2 + ) shift shift goto :parse_core_args ) -shift -goto :parse_core_args +if /I "%~1"=="--frontend-repo-url" ( + if "%~2"=="" goto :core_missing_frontend_repo_url + set "ARG_FRONTEND_REPO_URL=%~2" + if "!ARG_FRONTEND_REPO_URL:~0,2!"=="--" goto :core_missing_frontend_repo_url + set "DEVSYNC_VALIDATE_FRONTEND_REPO_URL=!ARG_FRONTEND_REPO_URL!" + powershell -NoProfile -Command "$url=$env:DEVSYNC_VALIDATE_FRONTEND_REPO_URL; if ($url -match '^(?:(?:https?|ssh|git)://\S+|[^@\s]+@[^:\s]+:.+)$') { exit 0 } else { exit 1 }" >nul 2>&1 + set "DEVSYNC_VALIDATE_FRONTEND_REPO_URL=" + if errorlevel 1 ( + echo [ERROR] Invalid value for --frontend-repo-url: !ARG_FRONTEND_REPO_URL! + echo Supported examples: + echo git@code.weoa.com:your-team/your-frontend.git + echo https://code.example.com/your-team/your-frontend.git + echo ssh://git@code.example.com/your-team/your-frontend.git + echo Tip: wrap the URL in double quotes when it contains special characters. + exit /b 2 + ) + set "USER_FRONTEND_REPO_URL=1" + shift + shift + goto :parse_core_args +) +echo [ERROR] Unknown --core parameter: %~1 +echo Supported optional parameters: --username, --frontend-repo-url +echo Example: install.bat --core D:\Workspace\mide --frontend-repo-url "git@code.weoa.com:your-team/your-frontend.git" +exit /b 2 + +:core_missing_frontend_repo_url +echo [ERROR] --frontend-repo-url requires a Git repository URL. +echo Example: install.bat --core D:\Workspace\mide --frontend-repo-url "git@code.weoa.com:your-team/your-frontend.git" +echo The repository URL is written to .claude\config\frontend-platform-config.json. +exit /b 2 :core_execute @@ -76,6 +118,12 @@ if not exist "%SCRIPT_DIR%\.claude" ( echo [--core] Install .claude to external project echo Source : %SCRIPT_DIR%\.claude echo Target : %TARGET_PROJECT%\.claude +if "%USER_FRONTEND_REPO_URL%"=="1" ( + echo Qianshou frontend repository: !ARG_FRONTEND_REPO_URL! +) else ( + echo Qianshou frontend repository: keep the default from the install package + echo Tip: use --frontend-repo-url GIT_URL to override it for this project. +) echo. REM Build xcopy exclude list - settings files are handled by merge_settings.py @@ -118,6 +166,41 @@ if exist "%MERGE_SCRIPT%" ( echo. ) +REM Configure Qianshou frontend repository for frontend-code-developer +set "FRONTEND_PLATFORM_CONFIG=%TARGET_PROJECT%\.claude\config\frontend-platform-config.json" +if "%USER_FRONTEND_REPO_URL%"=="1" ( + if not exist "%FRONTEND_PLATFORM_CONFIG%" ( + echo [ERROR] Cannot configure the Qianshou frontend repository because the config file is missing: + echo %FRONTEND_PLATFORM_CONFIG% + echo Please verify that the installation package contains .claude\config\frontend-platform-config.json. + exit /b 1 + ) + set "DEVSYNC_FRONTEND_CONFIG_PATH=%FRONTEND_PLATFORM_CONFIG%" + set "DEVSYNC_FRONTEND_REPO_URL=%ARG_FRONTEND_REPO_URL%" + powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $path=$env:DEVSYNC_FRONTEND_CONFIG_PATH; $url=$env:DEVSYNC_FRONTEND_REPO_URL; $config=Get-Content -LiteralPath $path -Raw -Encoding UTF8 | ConvertFrom-Json; if ($null -eq $config.defaults) { throw 'Missing defaults object' }; $config.defaults.repository_url=$url; $config | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8" + set "CONFIG_UPDATE_EXIT=!errorlevel!" + set "DEVSYNC_FRONTEND_CONFIG_PATH=" + set "DEVSYNC_FRONTEND_REPO_URL=" + if not "!CONFIG_UPDATE_EXIT!"=="0" ( + echo [ERROR] Failed to update the Qianshou frontend repository configuration. + echo Config: %FRONTEND_PLATFORM_CONFIG% + echo Please check that the file contains valid JSON and that you have write permission. + exit /b 1 + ) + echo [OK] Qianshou frontend repository configured successfully. + echo Repository: !ARG_FRONTEND_REPO_URL! + echo Config : %FRONTEND_PLATFORM_CONFIG% +) else ( + set "CURRENT_FRONTEND_REPO=" + if exist "%FRONTEND_PLATFORM_CONFIG%" ( + for /f "usebackq delims=" %%r in (`powershell -NoProfile -Command "$config=Get-Content -LiteralPath '%FRONTEND_PLATFORM_CONFIG%' -Raw -Encoding UTF8 | ConvertFrom-Json; $config.defaults.repository_url" 2^>nul`) do set "CURRENT_FRONTEND_REPO=%%r" + ) + echo [INFO] --frontend-repo-url was not provided; the packaged repository setting is retained. + if defined CURRENT_FRONTEND_REPO echo Current repository: !CURRENT_FRONTEND_REPO! + echo To override it, rerun with --frontend-repo-url "YOUR_GIT_URL". +) +echo. + REM Run MCP install set "MCP_INSTALLER=%TARGET_PROJECT%\.claude\mcp-installer\install.py" if not exist "%MCP_INSTALLER%" ( @@ -197,6 +280,11 @@ echo ============================================ echo .claude : %TARGET_PROJECT%\.claude echo CLAUDE.md : %TARGET_CLAUDE_MD% echo MCP config : %USERPROFILE%\.claude.json +if "%USER_FRONTEND_REPO_URL%"=="1" ( + echo Frontend repo: !ARG_FRONTEND_REPO_URL! +) else if defined CURRENT_FRONTEND_REPO ( + echo Frontend repo: !CURRENT_FRONTEND_REPO! ^(packaged default^) +) echo ============================================ pause exit /b 0