@ifrankwang/openspec-agents 0.127.0 → 0.130.0
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 +518 -138
- package/README.md +1 -1
- package/assets/agents/openspec-developer.md +2 -51
- package/assets/agents/openspec-reviewer.md +28 -20
- package/assets/workflows/task-simple.yaml +13 -12
- package/assets/workflows/task.yaml +4 -4
- package/dsh/cordis.patch.yml +29 -71
- package/package.json +1 -1
- package/src/adapters/deepseek-harness/index.ts +1 -1
- package/src/core/constants.ts +7 -0
- package/src/core/git.ts +61 -0
- package/src/core/tools/gate.ts +5 -0
- package/src/core/tools/lifecycle.ts +9 -8
- package/src/core/tools/schemas.ts +5 -1
- package/src/core/tools/submit.ts +64 -11
- package/src/core/views.ts +21 -0
- package/src/core/workflow/status.ts +41 -13
package/src/core/git.ts
CHANGED
|
@@ -371,3 +371,64 @@ export async function reconcileMainPollution(
|
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
|
+
|
|
375
|
+
/** worktree 自动提交(reviewer 家族文档直改兜底)结果。 */
|
|
376
|
+
export interface AutoCommitResult {
|
|
377
|
+
/** skipped:过滤 openspec/states/ 后无已跟踪文件变更(未触发提交);committed:add -u + commit 完成;
|
|
378
|
+
* failed:git 调用失败(降级返回,不抛异常)。 */
|
|
379
|
+
status: "skipped" | "committed" | "failed"
|
|
380
|
+
/** 纳入自动提交的已跟踪文件变更路径(skipped 时为空)。 */
|
|
381
|
+
files: string[]
|
|
382
|
+
/** status=failed 时的失败 stderr。 */
|
|
383
|
+
stderr?: string
|
|
384
|
+
/** 未跟踪新建文件(?? 条目)路径清单:add -u 不纳入提交,提示编排者另行处理。 */
|
|
385
|
+
untrackedFiles: string[]
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 自动提交 worktree 内已跟踪文件的未提交修改/删除(reviewer 家族文档直改兜底)。
|
|
390
|
+
*
|
|
391
|
+
* 触发场景:审查者/架构师按文档直改义务修正文档/注释后,由 opx_agent_submit 在 verdict
|
|
392
|
+
* 校验通过后调用——未提交修正会被下一轮工具检查点增量检测误判为新变更,也会在
|
|
393
|
+
* reconcileMainPollution 的干净树预检处死锁,提交时自动 commit 兜底。
|
|
394
|
+
*
|
|
395
|
+
* 语义:
|
|
396
|
+
* 1. `status --porcelain` 解析路径清单,过滤 `openspec/states/` 编排状态目录;过滤后无已跟踪
|
|
397
|
+
* 文件变更 → skipped(未触发)。
|
|
398
|
+
* 2. 有变更 → `add -u`(仅已跟踪文件的修改/删除,未跟踪新建文件不纳入,排除 openspec/states/)
|
|
399
|
+
* → `commit`(消息风格对齐既有 `docs(tasks):` / `docs(openspec):` 惯例)。
|
|
400
|
+
* 3. 任一 git 调用失败 → failed(含 stderr),不抛异常——调用方据此在返回体提示,不阻塞 verdict 写入。
|
|
401
|
+
*/
|
|
402
|
+
export async function autoCommitWorktreeChanges(
|
|
403
|
+
wtPath: string,
|
|
404
|
+
agent: string,
|
|
405
|
+
stepId: string,
|
|
406
|
+
): Promise<AutoCommitResult> {
|
|
407
|
+
const statusRes = await runGitChecked(wtPath, ["status", "--porcelain"])
|
|
408
|
+
if (!statusRes.success) {
|
|
409
|
+
return { status: "failed", files: [], untrackedFiles: [], stderr: statusRes.stderr }
|
|
410
|
+
}
|
|
411
|
+
const files: string[] = []
|
|
412
|
+
const untrackedFiles: string[] = []
|
|
413
|
+
for (const line of statusRes.stdout.split("\n").map((l) => l.trim()).filter(Boolean)) {
|
|
414
|
+
const path = parsePorcelainPaths(line)[0]
|
|
415
|
+
if (!path || path.startsWith("openspec/states/")) continue
|
|
416
|
+
if (line.startsWith("??")) untrackedFiles.push(path)
|
|
417
|
+
else files.push(path)
|
|
418
|
+
}
|
|
419
|
+
if (files.length === 0) return { status: "skipped", files: [], untrackedFiles }
|
|
420
|
+
|
|
421
|
+
const addResult = await runGitChecked(wtPath, ["add", "-u", "--", ".", ":(exclude)openspec/states"])
|
|
422
|
+
if (!addResult.success) {
|
|
423
|
+
return { status: "failed", files, untrackedFiles, stderr: addResult.stderr }
|
|
424
|
+
}
|
|
425
|
+
const commitResult = await runGitChecked(wtPath, [
|
|
426
|
+
"commit",
|
|
427
|
+
"-m",
|
|
428
|
+
`docs(opx): direct fixes by ${agent} (${stepId})`,
|
|
429
|
+
])
|
|
430
|
+
if (!commitResult.success) {
|
|
431
|
+
return { status: "failed", files, untrackedFiles, stderr: commitResult.stderr }
|
|
432
|
+
}
|
|
433
|
+
return { status: "committed", files, untrackedFiles }
|
|
434
|
+
}
|
package/src/core/tools/gate.ts
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* {"item":"<对应必做项>","category":"<降级类别>","adjudication":"user_response|unattended_auto|env_unavailable","note":"<说明>"}
|
|
12
12
|
* adjudication 裁定方式枚举:user_response=用户答复、unattended_auto=无人值守自动降级、
|
|
13
13
|
* env_unavailable=环境不可用(note 须记录尝试)。
|
|
14
|
+
*
|
|
15
|
+
* 必做覆盖口径:核验申报(completed=true + 描述注明核验方式与抽验样本,step 名首段 token 命中必做项)
|
|
16
|
+
* 是高成本必做项的合法覆盖形态——仅限 workflow 指令白名单限定的高成本必做项(deep_scan),低成本
|
|
17
|
+
* 必做项必须实跑后申报。覆盖判定只认 step 名 token 命中,不区分实跑与核验形态(机制层不拦截低成本项
|
|
18
|
+
* 误用核验申报形态,白名单约束由 workflow 指令与门禁错误提示承载)。
|
|
14
19
|
*/
|
|
15
20
|
import {
|
|
16
21
|
scanSkillTags,
|
|
@@ -641,16 +641,17 @@ export async function statusExecute(params: StatusParams, ctx: ToolContext): Pro
|
|
|
641
641
|
const tg = findTaskGroup(state, state.taskGroupId)
|
|
642
642
|
// 主仓库 openspec 污染诊断(56ddfe9 意图):编排者分派视图展示主仓库污染,供编排者人工核对
|
|
643
643
|
const mainPollution = ctx.orchestrator ? await detectMainRepoPollution(ctx.worktree) : null
|
|
644
|
-
// tool review 检查点增量检测(A4):
|
|
645
|
-
//
|
|
646
|
-
//
|
|
644
|
+
// tool review 检查点增量检测(A4):full 的 verify_tool(reviewer-tool)与 simple 的 quality_review
|
|
645
|
+
// (合并审查者 openspec-reviewer)工作视图均按「检查点 → 当前 HEAD」区间变更分流。渲染层为同步函数,
|
|
646
|
+
// 此处预计算后经 WorkflowStatusViewOptions 传入。仅推荐分派对应 agent 时计算(verify_tool → reviewer-tool、
|
|
647
|
+
// quality_review → openspec-reviewer),其余角色/step 不产生额外 git 调用。
|
|
647
648
|
let toolChanges: DetectChangesResult | undefined
|
|
648
|
-
|
|
649
|
-
agentToReviewLayer(agent) === "tool" &&
|
|
649
|
+
const changesEvidenceStep =
|
|
650
650
|
rec.status === "recommend" &&
|
|
651
|
-
rec.
|
|
652
|
-
|
|
653
|
-
|
|
651
|
+
rec.agents.includes(agent) &&
|
|
652
|
+
((agentToReviewLayer(agent) === "tool" && rec.stepId === "verify_tool") ||
|
|
653
|
+
(agent === "openspec-reviewer" && rec.stepId === "quality_review"))
|
|
654
|
+
if (changesEvidenceStep) {
|
|
654
655
|
const wtPath = typeof item.metadata["worktree_path"] === "string" ? item.metadata["worktree_path"] : undefined
|
|
655
656
|
if (wtPath) {
|
|
656
657
|
const checkpoint =
|
|
@@ -333,7 +333,11 @@ export const agentSubmitSchema: JSONSchema = {
|
|
|
333
333
|
description: "implement step:已完成的 task id(覆盖门禁:全部 open/rejected task 必须被覆盖)",
|
|
334
334
|
items: { type: "string" },
|
|
335
335
|
},
|
|
336
|
-
test_results: {
|
|
336
|
+
test_results: {
|
|
337
|
+
type: "string",
|
|
338
|
+
description:
|
|
339
|
+
"implement / verify_tool step:测试运行结果摘要(implement 为接口测试执行结果——附执行顺序与覆盖接口清单;verify_tool 为 UT 运行结果摘要);quality_review(simple 合并审查)下由开发者在 implement 申报,审查者提交时不生效",
|
|
340
|
+
},
|
|
337
341
|
validation_steps: {
|
|
338
342
|
type: "array",
|
|
339
343
|
description: "review step:验证步骤执行摘要",
|
package/src/core/tools/submit.ts
CHANGED
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
getStepVerdict, clearStepTags, isBlockingSeverity, isInfoSeverity, isTerminalPhase, applyTransition,
|
|
12
12
|
} from "../workflow/engine.ts"
|
|
13
13
|
import { resetReviewTagsOnFix, dedupeNewChildren, resolveChildIssueFields } from "../workflow/reset.ts"
|
|
14
|
-
import { agentToReviewDimension, agentToReviewLayer, readIssueSource } from "../constants.ts"
|
|
14
|
+
import { agentToReviewDimension, agentToReviewLayer, isReviewerFamily, readIssueSource } from "../constants.ts"
|
|
15
15
|
import { REVIEW_DIMENSIONS } from "../types.ts"
|
|
16
16
|
import type { Dimension } from "../types.ts"
|
|
17
17
|
import { taskChildrenOf, taskChildById, normalizeTaskChildIds, taskListOf, issueChildrenOf } from "../task-children.ts"
|
|
18
|
-
import { reconcileMainPollution, getCurrentHead, isWorktreeClean } from "../git.ts"
|
|
18
|
+
import { autoCommitWorktreeChanges, reconcileMainPollution, getCurrentHead, isWorktreeClean } from "../git.ts"
|
|
19
19
|
import { assertIssueFilesWithin } from "../paths.ts"
|
|
20
20
|
import {
|
|
21
21
|
readStateByWorktree, writeState, getLockPath, acquireLock, releaseLock,
|
|
@@ -264,7 +264,8 @@ function handleAnalyzeParams(item: WorkItem, params: AgentSubmitParams): void {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
/** implement step 参数处理:blocker(on_fail)、completed_task_ids + 覆盖门禁、self_check_results
|
|
267
|
+
/** implement step 参数处理:blocker(on_fail)、completed_task_ids + 覆盖门禁、self_check_results /
|
|
268
|
+
* test_results 申报存档。 */
|
|
268
269
|
function handleImplementParams(item: WorkItem, params: AgentSubmitParams): void {
|
|
269
270
|
if (params.blocker) {
|
|
270
271
|
if (params.verdict !== "failed") {
|
|
@@ -311,6 +312,11 @@ function handleImplementParams(item: WorkItem, params: AgentSubmitParams): void
|
|
|
311
312
|
if (params.self_check_results) {
|
|
312
313
|
item.metadata["self_check_results"] = params.self_check_results
|
|
313
314
|
}
|
|
315
|
+
// test_results 为 dev 申报的接口测试执行结果存档(simple 模式 quality_review 视图「开发者自检申报」
|
|
316
|
+
// 区块渲染源),与 self_check_results 同为跨角色可见的正式提交参数存档。
|
|
317
|
+
if (params.test_results) {
|
|
318
|
+
item.metadata["test_results"] = params.test_results
|
|
319
|
+
}
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
/** 把 boundary_expansion 的目录/包合并进执行边界(metadata 为事实源,缺失则忽略)。 */
|
|
@@ -395,7 +401,7 @@ function assertFailedHasReason(
|
|
|
395
401
|
}
|
|
396
402
|
}
|
|
397
403
|
|
|
398
|
-
/** review step(verify_tool/verify_task/verify_quality)参数处理。 */
|
|
404
|
+
/** review step(verify_tool/verify_task/verify_quality/quality_review)参数处理。 */
|
|
399
405
|
function handleReviewParams(
|
|
400
406
|
item: WorkItem,
|
|
401
407
|
params: AgentSubmitParams,
|
|
@@ -417,7 +423,10 @@ function handleReviewParams(
|
|
|
417
423
|
mergeBoundaryInto(item, params.boundary_expansion)
|
|
418
424
|
}
|
|
419
425
|
|
|
420
|
-
|
|
426
|
+
// test_results 透传:full 模式 verify_tool 的 UT 摘要写入不受影响;simple 模式 quality_review 下
|
|
427
|
+
// test_results 是 dev 申报的接口测试结果存档(quality_review 视图「开发者自检申报」区块渲染源),
|
|
428
|
+
// 合并审查者提交不得覆盖 dev 申报(spec 契约:申报区块语义为 dev 产出)。
|
|
429
|
+
if (params.test_results && stepId !== "quality_review") item.metadata["test_results"] = params.test_results
|
|
421
430
|
|
|
422
431
|
if (params.validation_steps) {
|
|
423
432
|
// 结构化 skip_reason 校验(升级既有「非空即可」):未完成项必须带合法结构化降级声明
|
|
@@ -428,6 +437,9 @@ function handleReviewParams(
|
|
|
428
437
|
// 必做清单覆盖度门禁:verdict=passed 且非补交豁免(仅裁定参数)时,validation_steps 须逐项覆盖
|
|
429
438
|
// 当前 agent 能力集解析出的质量门 skill 必做清单(must_do)。遗漏必做项且无有效降级证据 → 拒绝提交。
|
|
430
439
|
// 未声明 must_do 的 skill / 解析不到质量门 skill 的 step 自动跳过(不误伤 verify_task/verify_quality)。
|
|
440
|
+
// 覆盖口径:核验申报(completed=true + 描述注明核验方式与抽验样本,step 名首段 token 命中必做项)是
|
|
441
|
+
// 高成本必做项的合法覆盖形态,仅限 workflow 指令白名单限定的高成本必做项(deep_scan);低成本必做项
|
|
442
|
+
// 必须实跑后申报(机制层只认 token 命中不区分形态,白名单口径在错误提示中明示)。
|
|
431
443
|
if (params.verdict === "passed" && !opts.skipMustDoGate) {
|
|
432
444
|
const uncovered = uncoveredMustDo(opts.capabilityTags, params.validation_steps)
|
|
433
445
|
if (uncovered.length > 0) {
|
|
@@ -435,7 +447,8 @@ function handleReviewParams(
|
|
|
435
447
|
`提交的验证步骤未覆盖质量门 skill 必做清单,缺少以下必做项:${uncovered.join("、")}。\n` +
|
|
436
448
|
`请按已加载质量门类 skill 的必做清单逐项执行并逐项申报结果(completed=true);` +
|
|
437
449
|
`确实无法执行的必做项须以结构化 skip_reason 申报降级理由` +
|
|
438
|
-
`(格式:${SKIP_REASON_FORMAT})。`
|
|
450
|
+
`(格式:${SKIP_REASON_FORMAT})。` +
|
|
451
|
+
`低成本必做项必须实跑后申报;核验申报仅限 workflow 指令白名单限定的高成本必做项。`
|
|
439
452
|
)
|
|
440
453
|
}
|
|
441
454
|
}
|
|
@@ -734,6 +747,33 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
|
|
|
734
747
|
newChildren: accepted,
|
|
735
748
|
})
|
|
736
749
|
|
|
750
|
+
// reviewer 家族(openspec-architect + 全部审查者逻辑身份)worktree 直改修正自动提交兜底:
|
|
751
|
+
// 审查者/架构师按文档直改义务修正文档/注释后,提交时自动 commit(openspec/states/ 编排状态
|
|
752
|
+
// 除外,仅纳入已跟踪文件的修改/删除)。
|
|
753
|
+
// 时序硬约束:必须在 _tool_review_checkpoint 捕获之前(否则修正会被下一轮增量检测误判为新
|
|
754
|
+
// 变更)、必须在 reconcileMainPollution 之前(其干净树预检要求 worktree 无未提交内容——架构师
|
|
755
|
+
// 身份的 worktree md 修正无人提交会在该预检处死锁)。
|
|
756
|
+
// 硬边界(防事故):自动提交目标恒为 wtPath(任务组 worktree),严禁对 ctx.worktree(主仓库)
|
|
757
|
+
// 执行 add/commit。失败不阻塞 verdict 写入,仅在返回体提示编排者安排补提交。
|
|
758
|
+
const autoCommitLines: string[] = []
|
|
759
|
+
if (isReviewerFamily(ctx.agent)) {
|
|
760
|
+
const ac = await autoCommitWorktreeChanges(wtPath, ctx.agent, params.step_id)
|
|
761
|
+
if (ac.status === "committed") {
|
|
762
|
+
autoCommitLines.push(
|
|
763
|
+
`- ✅ worktree 直改修正已自动提交(${ac.files.length} 个文件,commit message: \`docs(opx): direct fixes by ${ctx.agent} (${params.step_id})\`)`,
|
|
764
|
+
)
|
|
765
|
+
} else if (ac.status === "failed") {
|
|
766
|
+
autoCommitLines.push(
|
|
767
|
+
`- ⚠️ worktree 直改修正自动提交失败:${ac.stderr || "未知错误"}。请编排者安排补提交(worktree 内 git add -u 后 commit)`,
|
|
768
|
+
)
|
|
769
|
+
}
|
|
770
|
+
if (ac.untrackedFiles.length > 0) {
|
|
771
|
+
autoCommitLines.push(
|
|
772
|
+
`- ⚠️ 存在未跟踪新建文件未纳入自动提交(须纳入版本控制时由编排者/开发者处理):${ac.untrackedFiles.join("、")}`,
|
|
773
|
+
)
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
737
777
|
// arch_submit 主仓库污染文档自动合并兜底(56ddfe9 意图):analyze step(架构师)以 passed 提交后,
|
|
738
778
|
// 若主仓库本 change 目录下存在未提交 openspec 文档污染,并入 worktree 分支并清理主仓库工作树。
|
|
739
779
|
if (
|
|
@@ -750,10 +790,15 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
|
|
|
750
790
|
})
|
|
751
791
|
}
|
|
752
792
|
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
//
|
|
756
|
-
|
|
793
|
+
// 审查检查点增量检测(A3):full 的 verify_tool(reviewer-tool)与 simple 的 quality_review
|
|
794
|
+
// (合并审查者 openspec-reviewer)提交成功后记录当前 HEAD,供下次重进该 step 时仅对比「检查点 →
|
|
795
|
+
// 当前 HEAD」区间的变更(无代码/配置变更时直提 passed,省去全量工具扫描)。两者写同一 metadata key
|
|
796
|
+
// `_tool_review_checkpoint`,但 simple / full 两 workflow 无对方 step,读写互不交叉。写入位于
|
|
797
|
+
// submitForStep 成功与 writeState 之间,任何异常路径均不落盘(零状态变更)。
|
|
798
|
+
if (
|
|
799
|
+
(params.step_id === "verify_tool" && agentToReviewLayer(ctx.agent) === "tool") ||
|
|
800
|
+
(params.step_id === "quality_review" && ctx.agent === "openspec-reviewer")
|
|
801
|
+
) {
|
|
757
802
|
const head = await getCurrentHead(wtPath)
|
|
758
803
|
if (head) item.metadata["_tool_review_checkpoint"] = head
|
|
759
804
|
}
|
|
@@ -766,7 +811,15 @@ export async function agentSubmitExecute(params: AgentSubmitParams, ctx: ToolCon
|
|
|
766
811
|
}
|
|
767
812
|
|
|
768
813
|
await writeState(ctx.worktree, state)
|
|
769
|
-
return SUBMIT_OK_MESSAGE
|
|
814
|
+
if (autoCommitLines.length === 0) return SUBMIT_OK_MESSAGE
|
|
815
|
+
return [
|
|
816
|
+
`## ✅ ${SUBMIT_OK_MESSAGE}`,
|
|
817
|
+
"",
|
|
818
|
+
"### worktree 自动提交",
|
|
819
|
+
"",
|
|
820
|
+
...autoCommitLines,
|
|
821
|
+
"",
|
|
822
|
+
].join("\n")
|
|
770
823
|
} finally {
|
|
771
824
|
releaseLock(lockPath)
|
|
772
825
|
}
|
package/src/core/views.ts
CHANGED
|
@@ -165,6 +165,27 @@ export function renderAgentSummaries(agentSummaries: Record<string, string> | un
|
|
|
165
165
|
return lines
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/** 开发者自检申报渲染。数据源为 workItem metadata 的 self_check_results / test_results(提交参数的存档),
|
|
169
|
+
* 分「自检申报(self_check_results)」与「接口测试结果(test_results)」两段渲染,两者皆缺失/为空时
|
|
170
|
+
* 返回空数组(不渲染空区块)。
|
|
171
|
+
* 与 renderAgentSummaries 的语义区分:会话摘要按角色隔离(辅助记忆,不跨 agent 传递);自检申报是
|
|
172
|
+
* 正式提交参数的存档,跨角色可见是设计意图——reviewer 分级复验以 dev 申报为事实输入(低成本实跑对照、
|
|
173
|
+
* 高成本项核验申报+抽样重放),不违反「子代理上下文不得转述」:该条约束的是编排者在分派 prompt 中
|
|
174
|
+
* 转述动态上下文,视图渲染动态上下文本就是 opx_status 工具本职。 */
|
|
175
|
+
export function renderDevSelfCheckDeclaration(metadata: Record<string, unknown>): string[] {
|
|
176
|
+
const selfCheck = typeof metadata["self_check_results"] === "string" ? metadata["self_check_results"].trim() : ""
|
|
177
|
+
const testResults = typeof metadata["test_results"] === "string" ? metadata["test_results"].trim() : ""
|
|
178
|
+
if (selfCheck === "" && testResults === "") return []
|
|
179
|
+
const lines: string[] = ["## 开发者自检申报", ""]
|
|
180
|
+
if (selfCheck !== "") {
|
|
181
|
+
lines.push("**自检申报(self_check_results)**:", "", selfCheck, "")
|
|
182
|
+
}
|
|
183
|
+
if (testResults !== "") {
|
|
184
|
+
lines.push("**接口测试结果(test_results)**:", "", testResults, "")
|
|
185
|
+
}
|
|
186
|
+
return lines
|
|
187
|
+
}
|
|
188
|
+
|
|
168
189
|
// ─── 占位符插值层 ───
|
|
169
190
|
|
|
170
191
|
/** 插值白名单:仅这些 key 可被 {{key}} 占位符引用,其余一律保留原文(防配置注入任意动态值)。 */
|
|
@@ -12,7 +12,7 @@ import { resolveChildIssueFields } from "./reset.ts"
|
|
|
12
12
|
import { taskListOf, issueChildrenOf } from "../task-children.ts"
|
|
13
13
|
import {
|
|
14
14
|
renderSkillSuggestions, renderEfficiencySteps, renderWorktreeSection,
|
|
15
|
-
renderAgentSummaries, renderTaskItem, formatFilePath, formatSeverity,
|
|
15
|
+
renderAgentSummaries, renderDevSelfCheckDeclaration, renderTaskItem, formatFilePath, formatSeverity,
|
|
16
16
|
isWorktreeReady, renderWorktreeNotReady, interpolateText, renderStateMismatchDiagnostic,
|
|
17
17
|
} from "../views.ts"
|
|
18
18
|
import { resolveMustDoForCaps, SKIP_REASON_FORMAT } from "../tools/gate.ts"
|
|
@@ -547,13 +547,25 @@ function renderAgentWorking(
|
|
|
547
547
|
}
|
|
548
548
|
lines.push(...renderStepSemantics(step, common, stepCtx))
|
|
549
549
|
lines.push(...renderWorktreeSection(state, tg, { showNamespace: true, showPort: true }))
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
//
|
|
554
|
-
//
|
|
550
|
+
// 证据区块注入:展示检查点增量口径的本次变更证据(文件清单 + 区间 diff 命令),与 Worktree 区块
|
|
551
|
+
// 「变更范围」(baseRef..HEAD 整个 change 累计口径)区分,供审查者审查变更内容后按操作指引裁量是否可免
|
|
552
|
+
// 全量工具检查(裁量语义由 workflow 配置单源承载,此处仅渲染事实证据)。
|
|
553
|
+
// - verify_tool(full 分支③有变更 → 全量路径):仅 reviewer-tool + 已预计算变更检测结果
|
|
554
|
+
// (hasNonDocChange=true)时渲染;分支①②提前返回、toolChanges 缺省路径不经过此处,不误伤其他 agent/step。
|
|
555
|
+
// - quality_review(simple 合并审查):toolChanges 已预计算即渲染,不判 hasNonDocChange——simple 无
|
|
556
|
+
// 三分支替换式视图,无变更时也渲染区块(文件清单处显式「未检出」),给「无变更直提」分流显式视觉信号。
|
|
555
557
|
if (step?.id === "verify_tool" && agentToReviewLayer(ctxAgent) === "tool" && toolChanges?.hasNonDocChange) {
|
|
556
|
-
lines.push(...renderToolChangesEvidence(item, tg, toolChanges))
|
|
558
|
+
lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "tool_review"))
|
|
559
|
+
}
|
|
560
|
+
if (step?.id === "quality_review" && toolChanges) {
|
|
561
|
+
lines.push(...renderToolChangesEvidence(item, tg, toolChanges, "merged_review"))
|
|
562
|
+
}
|
|
563
|
+
// simple 合并审查(quality_review)证据注入:渲染提交参数存档的自检申报(self_check_results /
|
|
564
|
+
// test_results),作为分级复验的事实输入——低成本实跑对照、高成本项核验申报+抽样重放均以该申报为
|
|
565
|
+
// 证据源。仅 quality_review step 渲染(消费方是 reviewer);implement 视图与其余 step 视图不受影响
|
|
566
|
+
//(dev 自己刚产出该申报,无需回显)。
|
|
567
|
+
if (step?.id === "quality_review") {
|
|
568
|
+
lines.push(...renderDevSelfCheckDeclaration(item.metadata))
|
|
557
569
|
}
|
|
558
570
|
lines.push(...renderAgentSummaries(readAgentSummaries(item), ctxAgent))
|
|
559
571
|
lines.push(...renderStepContext(item, step, ctxAgent, state, exemptionCtx))
|
|
@@ -799,26 +811,42 @@ function renderToolChildren(item: WorkItem, ctxAgent: string, exemptionCtx?: Exe
|
|
|
799
811
|
return lines
|
|
800
812
|
}
|
|
801
813
|
|
|
802
|
-
/** verify_tool
|
|
803
|
-
*
|
|
814
|
+
/** 变更证据区块的检查点口径措辞:tool_review=verify_tool(full,「工具检查」)、
|
|
815
|
+
* merged_review=quality_review(simple 合并审查,「合并审查」)。verify_tool 文案保持既有原文。 */
|
|
816
|
+
type ChangesEvidenceKind = "tool_review" | "merged_review"
|
|
817
|
+
|
|
818
|
+
const EVIDENCE_KIND_LABELS: Record<ChangesEvidenceKind, { title: string; lastCheck: string }> = {
|
|
819
|
+
tool_review: { title: "自上次工具检查", lastCheck: "上次工具检查" },
|
|
820
|
+
merged_review: { title: "自上次合并审查", lastCheck: "上次合并审查" },
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/** 证据区块:检查点增量口径的本次变更证据(文件清单 + 区间 diff 命令)。verify_tool 分支③(全量路径)
|
|
824
|
+
* 与 quality_review(simple 合并审查)共用,经 kind 区分口径措辞(「自上次工具检查」/「自上次合并审查」)。
|
|
825
|
+
* 有检查点时标注「本次为自上次审查(checkpoint..HEAD)增量区间」,与 Worktree 区块「变更范围」
|
|
804
826
|
* (baseRef..HEAD 整个 change 累计口径)区分;无检查点(首次进入)时标注基线兜底口径,与变更范围一致;
|
|
805
827
|
* 无检查点亦无基线基准时不渲染 diff 命令(无法界定区间)。仅渲染事实证据,不做变更性质判定——
|
|
806
828
|
* 裁量语义由 workflow 配置操作指引单源承载。 */
|
|
807
|
-
function renderToolChangesEvidence(
|
|
829
|
+
function renderToolChangesEvidence(
|
|
830
|
+
item: WorkItem,
|
|
831
|
+
tg: TaskGroupState,
|
|
832
|
+
toolChanges: DetectChangesResult,
|
|
833
|
+
kind: ChangesEvidenceKind,
|
|
834
|
+
): string[] {
|
|
835
|
+
const label = EVIDENCE_KIND_LABELS[kind]
|
|
808
836
|
const checkpoint =
|
|
809
837
|
typeof item.metadata["_tool_review_checkpoint"] === "string" ? item.metadata["_tool_review_checkpoint"] : undefined
|
|
810
838
|
const baseRef = tg.baseRef ?? undefined
|
|
811
839
|
const rangeRef = checkpoint ?? baseRef
|
|
812
|
-
const lines = [
|
|
840
|
+
const lines = [`## 本次变更证据(${label.title})`, ""]
|
|
813
841
|
if (checkpoint) {
|
|
814
842
|
lines.push(
|
|
815
|
-
`- **口径**:
|
|
843
|
+
`- **口径**: 本次为「${label.title}(${checkpoint}..HEAD)」的增量区间(含已提交与未提交的非 openspec 变更);` +
|
|
816
844
|
`与上方「变更范围」(${baseRef ?? "(无)"}..HEAD 整个 change 累计口径)不同`,
|
|
817
845
|
"",
|
|
818
846
|
)
|
|
819
847
|
} else if (baseRef) {
|
|
820
848
|
lines.push(
|
|
821
|
-
`- **口径**: 本次区间以基线(${baseRef}..HEAD
|
|
849
|
+
`- **口径**: 本次区间以基线(${baseRef}..HEAD)兜底,与上方「变更范围」一致(首次进入,无${label.lastCheck}记录;` +
|
|
822
850
|
"含已提交与未提交的非 openspec 变更)",
|
|
823
851
|
"",
|
|
824
852
|
)
|