@ifrankwang/openspec-agents 0.132.1 → 0.133.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp-server/cli.mjs +6950 -2070
- package/README.md +10 -0
- package/assets/agents/openspec-reviewer.md +1 -1
- package/assets/skills/orchestrator/SKILL.md +9 -0
- package/assets/workflows/review-simple.yaml +86 -0
- package/assets/workflows/review.yaml +124 -0
- package/dsh/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/src/adapters/plugin-common/index.ts +1 -1
- package/src/core/git.ts +124 -22
- package/src/core/state.ts +3 -0
- package/src/core/tools/lifecycle.ts +416 -31
- package/src/core/tools/schemas.ts +38 -5
- package/src/core/tools/types.ts +12 -2
- package/src/core/types.ts +26 -0
- package/src/core/views.ts +14 -1
- package/src/core/workflow/engine.ts +41 -9
- package/src/core/workflow/loader.ts +12 -3
- package/src/core/workflow/status.ts +76 -5
|
@@ -202,25 +202,58 @@ export const recoverySchema: JSONSchema = {
|
|
|
202
202
|
export const orchInitSchema: JSONSchema = {
|
|
203
203
|
type: "object",
|
|
204
204
|
properties: {
|
|
205
|
-
change_id: { type: "string", minLength: 1, description: "OpenSpec 变更 ID" },
|
|
205
|
+
change_id: { type: "string", minLength: 1, description: "OpenSpec 变更 ID(change 会话入口;与 review_scope 二选一)" },
|
|
206
206
|
task_group_id: {
|
|
207
207
|
type: "string",
|
|
208
208
|
minLength: 1,
|
|
209
|
-
description: "要初始化的任务组 ID
|
|
209
|
+
description: "要初始化的任务组 ID(change 会话入口)。无 recovery 重复调用当前组时保留进度;切换任务组时仅初始化目标组。独立审查会话恢复时可不传。",
|
|
210
210
|
},
|
|
211
211
|
base_branch: {
|
|
212
212
|
type: "string",
|
|
213
|
-
description: "基准分支名(如 main、develop),用于计算 merge-base 和 worktree fork 源。未传则自动从当前 git
|
|
213
|
+
description: "基准分支名(如 main、develop),用于计算 merge-base 和 worktree fork 源。未传则自动从当前 git 分支推导。仅 change 会话入口有效。",
|
|
214
214
|
},
|
|
215
215
|
recovery: recoverySchema,
|
|
216
216
|
mode: {
|
|
217
217
|
type: "string",
|
|
218
218
|
enum: ["full", "simple"],
|
|
219
219
|
description:
|
|
220
|
-
"
|
|
220
|
+
"流程模式选择(仅 change 会话入口有效,独立审查会话不使用):full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。首次新建编排状态时固化;已开始的变更仅在允许窗口内可更新:切换任务组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活);其余场景传不同 mode 将报错。",
|
|
221
|
+
},
|
|
222
|
+
review_scope: {
|
|
223
|
+
type: "object",
|
|
224
|
+
description:
|
|
225
|
+
"独立审查会话入口(不绑定 OpenSpec change):与 change_id/task_group_id 互斥——同传报错、都不传报错,两种入口二选一。会话 id 由审查范围确定性推导并在返回体回传,后续所有工具以 change_id 形式传入该会话 id。",
|
|
226
|
+
properties: {
|
|
227
|
+
scope_type: {
|
|
228
|
+
type: "string",
|
|
229
|
+
enum: ["pr", "full"],
|
|
230
|
+
description: "审查范围:pr=按 base_ref..head_ref 本地分支区间审查;full=全量代码库审查",
|
|
231
|
+
},
|
|
232
|
+
base_ref: {
|
|
233
|
+
type: "string",
|
|
234
|
+
minLength: 1,
|
|
235
|
+
description: "pr 形态的基准本地分支。缺省自动推导(优先 main、其次 master)。须为本地分支(origin/* 等远端 ref 不受支持——收尾合并只推进本地分支引用)",
|
|
236
|
+
},
|
|
237
|
+
head_ref: {
|
|
238
|
+
type: "string",
|
|
239
|
+
minLength: 1,
|
|
240
|
+
description: "pr 形态的头部本地分支(审查目标分支,pr 必传)。须为本地分支(origin/* 等远端 ref 不受支持)",
|
|
241
|
+
},
|
|
242
|
+
granularity: {
|
|
243
|
+
type: "string",
|
|
244
|
+
enum: ["simple", "thorough"],
|
|
245
|
+
description: "审查颗粒度:simple=单层合并审查;thorough=三层审查(工具检查→回归验证→五维度质量审查)",
|
|
246
|
+
},
|
|
247
|
+
fix: {
|
|
248
|
+
type: "string",
|
|
249
|
+
enum: ["none", "fix"],
|
|
250
|
+
description: "修复策略:none=只审不修(issue 报告即交付物,审查失败直接收口出报告);fix=审并修(审查失败回退修复,修复后重审)",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
required: ["scope_type", "granularity", "fix"],
|
|
254
|
+
additionalProperties: false,
|
|
221
255
|
},
|
|
222
256
|
},
|
|
223
|
-
required: ["change_id", "task_group_id"],
|
|
224
257
|
additionalProperties: false,
|
|
225
258
|
}
|
|
226
259
|
|
package/src/core/tools/types.ts
CHANGED
|
@@ -17,8 +17,18 @@ export interface ToolResult {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface InitParams {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/** change 会话入口必传;独立审查入口(review_scope)不传(会话 id 由 review_scope 推导并在返回体回传)。 */
|
|
21
|
+
change_id?: string
|
|
22
|
+
/** change 会话入口必传;独立审查会话用固定虚拟组 review(recovery 时可缺省)。 */
|
|
23
|
+
task_group_id?: string
|
|
24
|
+
/** 独立审查会话入口:与 change_id/task_group_id 互斥(同传报错、都不传报错)。 */
|
|
25
|
+
review_scope?: {
|
|
26
|
+
scope_type: "pr" | "full"
|
|
27
|
+
base_ref?: string
|
|
28
|
+
head_ref?: string
|
|
29
|
+
granularity: "simple" | "thorough"
|
|
30
|
+
fix: "none" | "fix"
|
|
31
|
+
}
|
|
22
32
|
base_branch?: string
|
|
23
33
|
recovery?: {
|
|
24
34
|
phase: BuildPhaseTarget
|
package/src/core/types.ts
CHANGED
|
@@ -34,6 +34,27 @@ export type QualityLayerProgress = Record<ReviewDimension, DimensionVerdict>
|
|
|
34
34
|
/** 流程模式:full(默认,analyze → implement → 三重审查+收尾验证)或 simple(implement → quality_review → done)。 */
|
|
35
35
|
export type WorkflowMode = "full" | "simple"
|
|
36
36
|
|
|
37
|
+
/** 独立审查会话的审查范围类型:pr=按 base..head 分支区间审查,full=全量代码库审查。 */
|
|
38
|
+
export type ReviewScopeType = "pr" | "full"
|
|
39
|
+
/** 独立审查颗粒度:thorough=三层审查(verify_tool → verify_task → verify_quality),simple=单层合并审查(quality_review)。 */
|
|
40
|
+
export type ReviewGranularity = "simple" | "thorough"
|
|
41
|
+
/** 独立审查修复策略:none=只审不修(issue 报告即交付物),fix=审并修(失败回 implement 修复闭环)。 */
|
|
42
|
+
export type ReviewFixPolicy = "none" | "fix"
|
|
43
|
+
|
|
44
|
+
/** 独立审查会话的审查范围(opx_orch_init 的 review_scope 参数固化,不绑定 OpenSpec change)。 */
|
|
45
|
+
export interface ReviewScope {
|
|
46
|
+
scopeType: ReviewScopeType
|
|
47
|
+
/** pr 形态的基准本地分支(init 推导后固化;full 形态缺省)。 */
|
|
48
|
+
baseRef?: string
|
|
49
|
+
/** pr 形态的头部本地分支(审查目标分支;full 形态缺省)。 */
|
|
50
|
+
headRef?: string
|
|
51
|
+
granularity: ReviewGranularity
|
|
52
|
+
fix: ReviewFixPolicy
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 独立审查会话的固定虚拟任务组 id(workItem id 为 task:review,无 tasks.md / task children)。 */
|
|
56
|
+
export const REVIEW_TASK_GROUP_ID = "review"
|
|
57
|
+
|
|
37
58
|
export interface ExecutionBoundary {
|
|
38
59
|
allowed_directories: string[]
|
|
39
60
|
allowed_packages: string[]
|
|
@@ -144,4 +165,9 @@ export interface OrchestrateState {
|
|
|
144
165
|
* (切组且其他任务组均已完成或从未激活,或 recovery.phase=task_analysis 重制当前组)内
|
|
145
166
|
* 可被 mode 参数更新。 */
|
|
146
167
|
mode?: WorkflowMode
|
|
168
|
+
/** 会话形态:review=独立审查会话(不绑定 OpenSpec change,无 tasks.md 解析 / mode 字段);
|
|
169
|
+
* 缺省视为 change 会话(读时兜底,不写回旧 state)。workflow 选择按 kind 优先于 mode。 */
|
|
170
|
+
kind?: "review"
|
|
171
|
+
/** kind=review 时的审查范围(granularity 决定 workflow 文件、fix 决定 fail 方向转移策略)。 */
|
|
172
|
+
reviewScope?: ReviewScope
|
|
147
173
|
}
|
package/src/core/views.ts
CHANGED
|
@@ -108,7 +108,20 @@ export function renderWorktreeSection(
|
|
|
108
108
|
if (tg.worktreePath) {
|
|
109
109
|
lines.push(`- **路径**: \`${tg.worktreePath}\``)
|
|
110
110
|
lines.push(`- **分支**: \`${tg.branchName || "(none)"}\``)
|
|
111
|
-
if (
|
|
111
|
+
if (state.kind === "review") {
|
|
112
|
+
// 独立审查会话:渲染「审查范围」锚点——pr 形态为 base..head 区间(base_ref 存 merge-base 锚点),
|
|
113
|
+
// full 形态为全量代码库(base_ref 不设,不得渲染为「未检出变更」)
|
|
114
|
+
if (state.reviewScope?.scopeType === "pr" && tg.baseRef) {
|
|
115
|
+
lines.push(
|
|
116
|
+
`- **审查范围**: PR 区间审查(${state.reviewScope.baseRef}..${state.reviewScope.headRef})——` +
|
|
117
|
+
`用 \`git -C ${tg.worktreePath} diff --name-only ${tg.baseRef}..HEAD\` 查询本区间已提交变更文件`,
|
|
118
|
+
)
|
|
119
|
+
} else {
|
|
120
|
+
lines.push("- **审查范围**: 全量代码库审查(无区间界定,工具检查与审查覆盖整个代码库)")
|
|
121
|
+
}
|
|
122
|
+
} else if (tg.baseRef) {
|
|
123
|
+
lines.push(`- **变更范围**: 用 \`git -C ${tg.worktreePath} diff --name-only ${tg.baseRef}..HEAD\` 查询本 change 全部已提交变更文件`)
|
|
124
|
+
}
|
|
112
125
|
lines.push("- **⚠️ 约束**: 所有读写和 git 操作均在此目录下进行;严禁直接修改主仓库/主分支路径下的文件(如 `<repo>/openspec/...`)")
|
|
113
126
|
lines.push("- **路径解析**: 推荐阅读文档均为相对 worktree 路径的引用,一律以 worktree 路径为基准解析,禁止从主仓库根目录解析")
|
|
114
127
|
} else {
|
|
@@ -310,6 +310,16 @@ export function suspendItem(item: WorkItem, reason: string): void {
|
|
|
310
310
|
|
|
311
311
|
export type TransitionDirection = "pass" | "fail"
|
|
312
312
|
|
|
313
|
+
/** fix=none 只审会话的修复策略 metadata 键(引擎无 state 访问,item.metadata 为引擎消费单一事实源,
|
|
314
|
+
* 由 init 从 state.reviewScope.fix 写入)。 */
|
|
315
|
+
export const REVIEW_FIX_POLICY_KEY = "review_fix_policy"
|
|
316
|
+
|
|
317
|
+
/** 读取 item 的独立审查修复策略(非 review 会话返回 undefined)。 */
|
|
318
|
+
export function reviewFixPolicyOf(item: WorkItem): "none" | "fix" | undefined {
|
|
319
|
+
const v = item.metadata[REVIEW_FIX_POLICY_KEY]
|
|
320
|
+
return v === "none" || v === "fix" ? v : undefined
|
|
321
|
+
}
|
|
322
|
+
|
|
313
323
|
export interface TransitionResult {
|
|
314
324
|
advanced: boolean
|
|
315
325
|
reason?: string
|
|
@@ -326,6 +336,18 @@ function phaseOrderIndex(phase: WorkItemPhase): number {
|
|
|
326
336
|
return WORK_ITEM_PHASES.indexOf(phase)
|
|
327
337
|
}
|
|
328
338
|
|
|
339
|
+
/** done 转移的统一收口:不走 forwardGatePassed(短路),置 done 前显式检查 task children 全部终态
|
|
340
|
+
* (未完成子任务不得收尾;issue blocking 检查已在 stepCanPass 处理;review 会话无 task children 天然通过)。 */
|
|
341
|
+
function applyDoneTransition(item: WorkItem): TransitionResult {
|
|
342
|
+
const unfinishedTasks = item.children.filter((child) => child.type === "task" && !isTerminalPhase(child.phase))
|
|
343
|
+
if (unfinishedTasks.length > 0) {
|
|
344
|
+
return { advanced: false, reason: "存在未完成的子任务(task children 未达终态),无法进入 done。" }
|
|
345
|
+
}
|
|
346
|
+
item.phase = "done"
|
|
347
|
+
item.currentStep = null
|
|
348
|
+
return { advanced: true, target: "done" }
|
|
349
|
+
}
|
|
350
|
+
|
|
329
351
|
/**
|
|
330
352
|
* 沿当前 step 的 transition 推进状态机。
|
|
331
353
|
* - done: item 进入终态(phase=done)并触发写回
|
|
@@ -346,16 +368,15 @@ export function applyTransition(
|
|
|
346
368
|
|
|
347
369
|
const target = direction === "pass" ? current.step.transitions.on_pass : current.step.transitions.on_fail
|
|
348
370
|
|
|
371
|
+
// fix=none 只审会话:fail 方向转移目标 implement 为拓扑统一声明的死 step(review workflow 的
|
|
372
|
+
// on_fail 一律写 implement),策略短路直达 done——不可拦截转移:failed tag 滞留会让审查者被
|
|
373
|
+
// 无限重派死锁。done 收口只检查 task children 终态,review 会话无 task children 天然通过,
|
|
374
|
+
// issue 报告即交付物(收尾 openIssues 门禁对 fix=none 放宽)。
|
|
375
|
+
if (direction === "fail" && target === "implement" && reviewFixPolicyOf(item) === "none") {
|
|
376
|
+
return applyDoneTransition(item)
|
|
377
|
+
}
|
|
349
378
|
if (target === "done") {
|
|
350
|
-
|
|
351
|
-
// (未完成子任务不得收尾;issue blocking 检查已在 stepCanPass 处理)。
|
|
352
|
-
const unfinishedTasks = item.children.filter((child) => child.type === "task" && !isTerminalPhase(child.phase))
|
|
353
|
-
if (unfinishedTasks.length > 0) {
|
|
354
|
-
return { advanced: false, reason: "存在未完成的子任务(task children 未达终态),无法进入 done。" }
|
|
355
|
-
}
|
|
356
|
-
item.phase = "done"
|
|
357
|
-
item.currentStep = null
|
|
358
|
-
return { advanced: true, target: "done" }
|
|
379
|
+
return applyDoneTransition(item)
|
|
359
380
|
}
|
|
360
381
|
if (target === "halt") {
|
|
361
382
|
suspendItem(item, "halt")
|
|
@@ -510,6 +531,17 @@ export function recommendForItem(item: WorkItem, workflow: LoadedWorkflow): Engi
|
|
|
510
531
|
}
|
|
511
532
|
}
|
|
512
533
|
|
|
534
|
+
// fix=none 只审会话防御:fail 方向已被策略短路映射 done,implement 逻辑上不可达;
|
|
535
|
+
// 若因状态异常(如 recovery 误置)落进 implement,不推荐分派(否则只审会话出现修复动作)。
|
|
536
|
+
if (current.step.id === "implement" && reviewFixPolicyOf(item) === "none") {
|
|
537
|
+
return {
|
|
538
|
+
status: "blocked",
|
|
539
|
+
stepId: current.step.id,
|
|
540
|
+
agents: [],
|
|
541
|
+
blockedReason: "只审(fix=none)会话不可进入 implement(修复步骤逻辑上不可达),请核对 state 后按需调用 opx_orch_init(recovery=...) 恢复。",
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
513
545
|
if (checkpointTriggered(item, workflow, current.step)) {
|
|
514
546
|
return {
|
|
515
547
|
status: "checkpoint",
|
|
@@ -54,12 +54,21 @@ export function resolveTaskWorkflowPath(moduleUrl: string): string {
|
|
|
54
54
|
export const TASK_WORKFLOW_PATH = resolveTaskWorkflowPath(import.meta.url)
|
|
55
55
|
/** simple 流程文件(implement → quality_review → done),与 task.yaml 同目录随插件 bundle 分发。 */
|
|
56
56
|
export const SIMPLE_WORKFLOW_PATH = resolveWorkflowFilePath(import.meta.url, "task-simple.yaml")
|
|
57
|
+
/** 独立审查会话(thorough 颗粒度)流程文件:三层审查 + implement 修复闭环。 */
|
|
58
|
+
export const REVIEW_WORKFLOW_PATH = resolveWorkflowFilePath(import.meta.url, "review.yaml")
|
|
59
|
+
/** 独立审查会话(simple 颗粒度)流程文件:单层合并审查 + implement 修复闭环。 */
|
|
60
|
+
export const REVIEW_SIMPLE_WORKFLOW_PATH = resolveWorkflowFilePath(import.meta.url, "review-simple.yaml")
|
|
57
61
|
|
|
58
62
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
63
|
+
* 选择 workflow 文件:kind 优先于 mode——
|
|
64
|
+
* - kind=review(独立审查会话,无 mode 字段):按 reviewScope.granularity 选 review.yaml / review-simple.yaml;
|
|
65
|
+
* - change 会话:mode=simple → task-simple.yaml,其余(full 或旧 state 缺 mode)→ task.yaml。
|
|
66
|
+
* 旧 state 无 mode 一律按 full 处理(读时兜底,不写回),与 D2 固化语义一致。
|
|
61
67
|
*/
|
|
62
|
-
export function resolveWorkflowPath(state: Pick<OrchestrateState, "mode">): string {
|
|
68
|
+
export function resolveWorkflowPath(state: Pick<OrchestrateState, "mode" | "kind" | "reviewScope">): string {
|
|
69
|
+
if (state.kind === "review") {
|
|
70
|
+
return state.reviewScope?.granularity === "simple" ? REVIEW_SIMPLE_WORKFLOW_PATH : REVIEW_WORKFLOW_PATH
|
|
71
|
+
}
|
|
63
72
|
return state.mode === "simple" ? SIMPLE_WORKFLOW_PATH : TASK_WORKFLOW_PATH
|
|
64
73
|
}
|
|
65
74
|
|
|
@@ -71,7 +71,7 @@ export function renderWorkflowStatusView(
|
|
|
71
71
|
return renderSuspended(item)
|
|
72
72
|
}
|
|
73
73
|
if (item.phase === "done" || item.phase === "cancelled") {
|
|
74
|
-
return renderTerminalPhase(item)
|
|
74
|
+
return renderTerminalPhase(item, options.state)
|
|
75
75
|
}
|
|
76
76
|
if (rec.status === "blocked") {
|
|
77
77
|
if (caller.orchestrator) {
|
|
@@ -155,8 +155,11 @@ function renderSuspended(item: WorkItem): string {
|
|
|
155
155
|
].join("\n")
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
/** 终态渲染:done 区分已完成(completed_at 已写)与待收尾;cancelled 呈现已取消。
|
|
159
|
-
|
|
158
|
+
/** 终态渲染:done 区分已完成(completed_at 已写)与待收尾;cancelled 呈现已取消。
|
|
159
|
+
* 独立审查会话(kind=review)待收口时渲染审查结果报告视图(issue 按严重级别/维度/文件位置归并,
|
|
160
|
+
* 只审模式的交付物)——fix=none 下 issue 停留 todo 态、step tag=failed、item=done 为合法组合,
|
|
161
|
+
* 报告视图不渲染豁免/复核操作提示(该通道在只审模式下是死路,无修复者消费裁定结论)。 */
|
|
162
|
+
function renderTerminalPhase(item: WorkItem, state?: OrchestrateState): string {
|
|
160
163
|
if (item.phase === "cancelled") {
|
|
161
164
|
return [
|
|
162
165
|
"# 🚫 任务组已取消",
|
|
@@ -165,6 +168,27 @@ function renderTerminalPhase(item: WorkItem): string {
|
|
|
165
168
|
"",
|
|
166
169
|
].join("\n")
|
|
167
170
|
}
|
|
171
|
+
if (state?.kind === "review") {
|
|
172
|
+
if (item.metadata["completed_at"] !== undefined) {
|
|
173
|
+
return [
|
|
174
|
+
"# ✅ 独立审查会话已完成",
|
|
175
|
+
"",
|
|
176
|
+
`- **完成时间**: ${item.metadata["completed_at"]}`,
|
|
177
|
+
"",
|
|
178
|
+
"审查结果摘要见收尾返回体(opx_orch_complete_task_group)。",
|
|
179
|
+
"",
|
|
180
|
+
].join("\n")
|
|
181
|
+
}
|
|
182
|
+
const lines = [
|
|
183
|
+
"# 🏁 独立审查已完成,待收尾",
|
|
184
|
+
"",
|
|
185
|
+
renderReviewReport(item),
|
|
186
|
+
"",
|
|
187
|
+
"全部审查层已收口。调用 `opx_orch_complete_task_group` 完成收尾(只审模式销毁 worktree 不合并,审并修模式合并回目标分支)。",
|
|
188
|
+
"",
|
|
189
|
+
]
|
|
190
|
+
return lines.join("\n")
|
|
191
|
+
}
|
|
168
192
|
if (item.metadata["completed_at"] !== undefined) {
|
|
169
193
|
return [
|
|
170
194
|
"# ✅ 任务组已完成",
|
|
@@ -183,6 +207,42 @@ function renderTerminalPhase(item: WorkItem): string {
|
|
|
183
207
|
].join("\n")
|
|
184
208
|
}
|
|
185
209
|
|
|
210
|
+
/** 独立审查报告视图:issue 按严重级别/维度/文件位置归并的最终报告形态(views 同构渲染共享)。 */
|
|
211
|
+
function renderReviewReport(item: WorkItem): string {
|
|
212
|
+
const issues = issueChildrenOf(item)
|
|
213
|
+
if (issues.length === 0) return "未报出任何 issue。"
|
|
214
|
+
const bySeverity = new Map<string, number>()
|
|
215
|
+
const byDimension = new Map<string, number>()
|
|
216
|
+
for (const c of issues) {
|
|
217
|
+
const sev = c.severity ?? "Info"
|
|
218
|
+
const f = resolveChildIssueFields(c)
|
|
219
|
+
bySeverity.set(sev, (bySeverity.get(sev) ?? 0) + 1)
|
|
220
|
+
byDimension.set(f.dimension, (byDimension.get(f.dimension) ?? 0) + 1)
|
|
221
|
+
}
|
|
222
|
+
const lines = [
|
|
223
|
+
"## 审查结果报告",
|
|
224
|
+
"",
|
|
225
|
+
`- **issue 总数**: ${issues.length}`,
|
|
226
|
+
`- **按严重级别**: ${[...bySeverity.entries()].map(([k, v]) => `${k}=${v}`).join("、")}`,
|
|
227
|
+
`- **按维度**: ${[...byDimension.entries()].map(([k, v]) => `${k}=${v}`).join("、")}`,
|
|
228
|
+
"",
|
|
229
|
+
]
|
|
230
|
+
for (const sev of ["Critical", "High", "Medium", "Low", "Info"]) {
|
|
231
|
+
const group = issues.filter((c) => (c.severity ?? "Info") === sev)
|
|
232
|
+
if (group.length === 0) continue
|
|
233
|
+
lines.push(`### ${sev}(${group.length} 个)`, "")
|
|
234
|
+
for (const c of group) {
|
|
235
|
+
const id = c.externalId ?? c.id.replace(/^issue:/, "")
|
|
236
|
+
const f = resolveChildIssueFields(c)
|
|
237
|
+
lines.push(`- Issue #${id} | 维度:${f.dimension}${f.file ? ` | \`${formatFilePath(f.file, f.line)}\`` : ""}`)
|
|
238
|
+
lines.push(` - 描述:${c.description}`)
|
|
239
|
+
if (typeof c.metadata["suggestion"] === "string") lines.push(` - 建议:${c.metadata["suggestion"]}`)
|
|
240
|
+
}
|
|
241
|
+
lines.push("")
|
|
242
|
+
}
|
|
243
|
+
return lines.join("\n")
|
|
244
|
+
}
|
|
245
|
+
|
|
186
246
|
/** blocked 视图:调用者若为当前 step 轮次 agent 用 blocked_agent 文案,否则用通用 blocked 文案。
|
|
187
247
|
* verify_* 层当前 agent 且存在本层待复核(review 态)blocking issue 时,列出待复核清单并给出
|
|
188
248
|
* recheck_adjudications 自助恢复指引(上次提交漏带复核导致门禁阻塞的可补交解除);
|
|
@@ -499,7 +559,8 @@ function renderAgentWorking(
|
|
|
499
559
|
|
|
500
560
|
// tool review 检查点增量三分支(A4):仅 verify_tool 的 reviewer-tool 且提供了变更检测结果时生效;
|
|
501
561
|
// toolChanges 缺省(未预计算)时维持既有全量渲染,不误伤其他 agent/step 的通用渲染。
|
|
502
|
-
|
|
562
|
+
// 独立审查 full 形态(scopeFull 哨兵)不走三分支——审查锚点为全量代码库,非「未检出变更」。
|
|
563
|
+
if (step?.id === "verify_tool" && agentToReviewLayer(ctxAgent) === "tool" && toolChanges && !toolChanges.scopeFull) {
|
|
503
564
|
const { active, pending } = toolPendingChildren(item, ctxAgent)
|
|
504
565
|
if (!toolChanges.hasNonDocChange && active.length === 0 && pending.length === 0) {
|
|
505
566
|
return renderToolDirectSubmit(exemptedHits)
|
|
@@ -564,7 +625,9 @@ function renderAgentWorking(
|
|
|
564
625
|
// test_results),作为分级复验的事实输入——低成本实跑对照、高成本项核验申报+抽样重放均以该申报为
|
|
565
626
|
// 证据源。仅 quality_review step 渲染(消费方是 reviewer);implement 视图与其余 step 视图不受影响
|
|
566
627
|
//(dev 自己刚产出该申报,无需回显)。
|
|
567
|
-
|
|
628
|
+
// 开发者自检申报区块仅 change 会话渲染(simple 模式 quality_review 的分级复验事实输入);
|
|
629
|
+
// 独立审查会话无该数据源(无 dev 实施申报语义),不渲染空区块。
|
|
630
|
+
if (step?.id === "quality_review" && state.kind !== "review") {
|
|
568
631
|
lines.push(...renderDevSelfCheckDeclaration(item.metadata))
|
|
569
632
|
}
|
|
570
633
|
lines.push(...renderAgentSummaries(readAgentSummaries(item), ctxAgent))
|
|
@@ -843,6 +906,14 @@ function renderToolChangesEvidence(
|
|
|
843
906
|
const baseRef = tg.baseRef ?? undefined
|
|
844
907
|
const rangeRef = checkpoint ?? baseRef
|
|
845
908
|
const lines = [`## 本次变更证据(${label.title})`, ""]
|
|
909
|
+
// 独立审查 full 形态哨兵:审查锚点为全量代码库(无区间可界定),不渲染「未检出变更」误报
|
|
910
|
+
if (toolChanges.scopeFull) {
|
|
911
|
+
lines.push(
|
|
912
|
+
"- **口径**: 全量代码库审查(独立审查 full 形态)——无区间界定,工具检查与审查覆盖整个代码库",
|
|
913
|
+
"",
|
|
914
|
+
)
|
|
915
|
+
return [lines.join("\n")]
|
|
916
|
+
}
|
|
846
917
|
if (checkpoint) {
|
|
847
918
|
lines.push(
|
|
848
919
|
`- **口径**: 本次为「${label.title}(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` +
|