@fitlab-ai/agent-infra 0.8.1 → 0.8.3
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/cli.ts +3 -3
- package/dist/bin/cli.js +4 -4
- package/dist/lib/cp.js +1 -1
- package/dist/lib/decide.js +2 -2
- package/dist/lib/defaults.json +2 -0
- package/dist/lib/init.js +7 -7
- package/dist/lib/merge.js +1 -1
- package/dist/lib/prompt.js +1 -1
- package/dist/lib/run/index.js +7 -7
- package/dist/lib/run/prompt.js +1 -1
- package/dist/lib/sandbox/capture.js +5 -5
- package/dist/lib/sandbox/clipboard/bridge.js +48 -13
- package/dist/lib/sandbox/clipboard/index.js +5 -6
- package/dist/lib/sandbox/clipboard/linux.js +91 -0
- package/dist/lib/sandbox/clipboard/paths.js +1 -1
- package/dist/lib/sandbox/clipboard/win32.js +144 -0
- package/dist/lib/sandbox/commands/create.js +101 -66
- package/dist/lib/sandbox/commands/enter.js +9 -9
- package/dist/lib/sandbox/commands/list-running.js +2 -2
- package/dist/lib/sandbox/commands/ls.js +7 -7
- package/dist/lib/sandbox/commands/prune.js +8 -8
- package/dist/lib/sandbox/commands/rebuild.js +38 -50
- package/dist/lib/sandbox/commands/refresh.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +11 -11
- package/dist/lib/sandbox/commands/show.js +4 -4
- package/dist/lib/sandbox/commands/start.js +4 -4
- package/dist/lib/sandbox/commands/vm.js +4 -4
- package/dist/lib/sandbox/config.js +11 -5
- package/dist/lib/sandbox/constants.js +4 -1
- package/dist/lib/sandbox/credentials.js +1 -1
- package/dist/lib/sandbox/dotfiles.js +1 -1
- package/dist/lib/sandbox/engine.js +3 -3
- package/dist/lib/sandbox/engines/index.js +5 -5
- package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
- package/dist/lib/sandbox/image-build.js +80 -0
- package/dist/lib/sandbox/image-prune.js +2 -2
- package/dist/lib/sandbox/index.js +10 -10
- package/dist/lib/sandbox/managed-fs.js +1 -1
- package/dist/lib/sandbox/readme-scaffold.js +1 -1
- package/dist/lib/sandbox/runtimes/node20.dockerfile +1 -1
- package/dist/lib/sandbox/runtimes/node22.dockerfile +1 -1
- package/dist/lib/sandbox/tools.js +11 -2
- package/dist/lib/server/adapters/feishu/index.js +12 -3
- package/dist/lib/server/adapters/feishu/renderer.js +88 -0
- package/dist/lib/server/adapters/feishu/transport.js +9 -2
- package/dist/lib/server/daemon.js +64 -43
- package/dist/lib/server/display.js +83 -0
- package/dist/lib/server/index.js +2 -2
- package/dist/lib/server/process-control.js +2 -2
- package/dist/lib/server/protocol.js +22 -6
- package/dist/lib/server/streamer.js +5 -4
- package/dist/lib/task/commands/cat.js +2 -2
- package/dist/lib/task/commands/decisions.js +4 -4
- package/dist/lib/task/commands/files.js +3 -3
- package/dist/lib/task/commands/grep.js +3 -3
- package/dist/lib/task/commands/issue-body.js +4 -4
- package/dist/lib/task/commands/log.js +5 -5
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/commands/show.js +1 -1
- package/dist/lib/task/commands/status.js +71 -25
- package/dist/lib/task/index.js +9 -9
- package/dist/lib/task/resolve-ref.js +1 -1
- package/dist/lib/task/workflow-warnings.js +94 -0
- package/dist/lib/update.js +35 -4
- package/lib/defaults.json +2 -0
- package/lib/sandbox/clipboard/bridge.ts +50 -8
- package/lib/sandbox/clipboard/index.ts +5 -6
- package/lib/sandbox/clipboard/linux.ts +124 -0
- package/lib/sandbox/clipboard/win32.ts +173 -0
- package/lib/sandbox/commands/create.ts +123 -65
- package/lib/sandbox/commands/rebuild.ts +42 -54
- package/lib/sandbox/config.ts +14 -2
- package/lib/sandbox/constants.ts +4 -0
- package/lib/sandbox/image-build.ts +134 -0
- package/lib/sandbox/runtimes/node20.dockerfile +1 -1
- package/lib/sandbox/runtimes/node22.dockerfile +1 -1
- package/lib/sandbox/tools.ts +9 -0
- package/lib/server/adapters/_contract.ts +3 -0
- package/lib/server/adapters/feishu/index.ts +12 -2
- package/lib/server/adapters/feishu/renderer.ts +99 -0
- package/lib/server/adapters/feishu/transport.ts +19 -3
- package/lib/server/daemon.ts +74 -38
- package/lib/server/display.ts +136 -0
- package/lib/server/protocol.ts +20 -4
- package/lib/server/streamer.ts +5 -4
- package/lib/task/commands/log.ts +3 -3
- package/lib/task/commands/status.ts +102 -21
- package/lib/task/workflow-warnings.ts +121 -0
- package/lib/update.ts +34 -0
- package/package.json +3 -3
- package/templates/.agents/README.en.md +3 -3
- package/templates/.agents/README.zh-CN.md +3 -3
- package/templates/.agents/rules/create-issue.github.en.md +3 -3
- package/templates/.agents/rules/create-issue.github.zh-CN.md +3 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
- package/templates/.agents/rules/issue-sync.github.en.md +13 -0
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
- package/templates/.agents/rules/next-step-output.en.md +15 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
- package/templates/.agents/rules/pr-sync.github.en.md +17 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
- package/templates/.agents/scripts/validate-artifact.js +120 -0
- package/templates/.agents/scripts/workflow-warnings.js +290 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +118 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +118 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +33 -1
- package/templates/.agents/templates/task.en.md +7 -0
- package/templates/.agents/templates/task.zh-CN.md +7 -0
- package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
- package/templates/.github/workflows/metadata-sync.yml +5 -0
- package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# 下一步输出规则
|
|
2
2
|
|
|
3
|
-
本文件定义 skill「告知用户 /
|
|
3
|
+
本文件定义 skill「告知用户 / 下一步」输出的四类**相互独立**的规则(第 3 类仅 review-* 适用);渲染最终输出前先读取本文件并落实其中适用的规则:
|
|
4
4
|
|
|
5
5
|
1. **下一步输出结构**:「下一步」命令与「任务信息」段如何呈现任务 ID 形态(占位符 / 取短号 / 回退)。
|
|
6
6
|
2. **Agent 输出收尾行(Completed at)**:面向用户输出的**绝对最后一行**,**独立于「下一步」块**,正常 / 错误 / 早退路径都适用。
|
|
7
7
|
3. **人工裁决待办前置块**:仅 `review-analysis` / `review-plan` / `review-code`,且本阶段存在待裁决项(`{h} > 0`)时适用——在「下一步」命令前展开待裁决项并提示先完成裁决。
|
|
8
|
+
4. **Workflow Warnings 输出块**:当前 task.md 存在 `status=open` 的 `## 工作流告警` 行时适用——在所有常规信息和「下一步」命令之后、`Completed at` 之前输出告警摘要。
|
|
8
9
|
|
|
9
10
|
## 占位符语义
|
|
10
11
|
|
|
@@ -62,6 +63,19 @@ Completed at: YYYY-MM-DD HH:mm:ss
|
|
|
62
63
|
- 位置:必须是整段面向用户输出的最后一行,排在所有「下一步」命令之后。若某场景在命令之后还有条件性提醒行(如 manual-validation 提醒),收尾行排在该提醒行之后。
|
|
63
64
|
- 该行只用于终端扫视,不写入任何产物文件或 Issue/PR 评论;完成时刻的单一事实源仍是 task.md 的 Activity Log。
|
|
64
65
|
|
|
66
|
+
## Workflow Warnings 输出块
|
|
67
|
+
|
|
68
|
+
若当前任务的 `## 工作流告警` / `## Workflow Warnings` 表中存在 `status=open` 行,skill 最终输出必须在所有常规信息和「下一步」命令之后、`Completed at` 之前追加摘要块;无 open 告警时不输出本块。
|
|
69
|
+
|
|
70
|
+
格式:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
[ACTION REQUIRED] Workflow warnings are open:
|
|
74
|
+
- WW-N {code} ({target}): {action}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`severity=ACTION_REQUIRED` 的行使用 `[ACTION REQUIRED]`;只有 `IMPORTANT` 行时使用 `[IMPORTANT]`。`{code}`、`{target}`、`{action}` 直接来自告警表对应列。
|
|
78
|
+
|
|
65
79
|
## 人工裁决待办前置块(review-* 专用,{h} > 0 时)
|
|
66
80
|
|
|
67
81
|
本节是与上面两类规则**并列的第三类独立规则**,仅 `review-analysis` / `review-plan` / `review-code` 的「向用户汇报结论」步骤使用。
|
|
@@ -7,6 +7,7 @@ Read this file before syncing the single reviewer-facing PR summary comment.
|
|
|
7
7
|
Current callers:
|
|
8
8
|
- `create-pr`: create or update the summary comment when the PR is opened
|
|
9
9
|
- `commit`: refresh the summary comment on an existing PR when needed
|
|
10
|
+
- `complete-manual-validation`: update the summary comment in place after manual validation passes, and write a validation artifact that later aggregation can reuse
|
|
10
11
|
|
|
11
12
|
If another skill needs to refresh the PR summary later, point that skill's reference file at this rule first, then document only the skill-specific trigger and failure behavior there.
|
|
12
13
|
|
|
@@ -26,17 +27,22 @@ Aggregate the latest artifacts in the task directory:
|
|
|
26
27
|
- `plan.md` or the latest `plan-r{N}.md`
|
|
27
28
|
- `code.md` or the latest `code-r{N}.md`
|
|
28
29
|
- `review-code.md` or the latest `review-code-r{N}.md`
|
|
30
|
+
- `manual-validation.md` or the latest `manual-validation-r{N}.md`
|
|
29
31
|
|
|
30
32
|
Aggregation rules:
|
|
31
33
|
- extract 2-4 self-contained technical decisions from `plan*`
|
|
32
34
|
- build the review-history table from `review-code*` and `code*`
|
|
33
35
|
- extract the test summary from `code*`
|
|
36
|
+
- if the latest `manual-validation*` artifact has a passed verdict, render the manual-validation section first as `### ✅ Manual Validation Passed`, with a concise validation time, scope, and notes summary
|
|
34
37
|
- if one artifact class is missing, treat it as "no data for this stage" and continue
|
|
35
38
|
- Manual verification section: include only post-code-stage checks that still require a human to execute or judge and that the AI cannot close on its own.
|
|
36
39
|
- **Admission boundary**: the verification result depends on a real environment, permissions, account, external system, or human judgment, and cannot be closed by an agent rerunning tests, adding checks, or continuing the fix loop.
|
|
37
40
|
- **Sources**: `review-code*` "Manual Validation Items", plus `code*` items that satisfy the boundary above.
|
|
38
41
|
- **Wording**: each retained item must state at least "what to verify + location (file/change/scope) + why only a human can verify it".
|
|
39
|
-
- **
|
|
42
|
+
- **Rendering priority**:
|
|
43
|
+
1. the latest `manual-validation*` artifact has a passed verdict -> heading `### ✅ Manual Validation Passed` + validation summary.
|
|
44
|
+
2. no passed artifact and retained items exist -> heading `### ⚠️ Manual Verification Required` + item list.
|
|
45
|
+
3. no passed artifact and no retained items -> heading `### ✅ No Manual Verification Needed` and a single line `No items in this change require manual confirmation.`, with no item list.
|
|
40
46
|
|
|
41
47
|
## Comment Body Template
|
|
42
48
|
|
|
@@ -72,7 +78,7 @@ Use this canonical comment body template:
|
|
|
72
78
|
*Generated by {agent} · Internal tracking: {task-id}*
|
|
73
79
|
```
|
|
74
80
|
|
|
75
|
-
> Render `{manual-validation-section}` per the "manual verification section" aggregation rule above:
|
|
81
|
+
> Render `{manual-validation-section}` per the "manual verification section" aggregation rule above: passed artifact → `### ✅ Manual Validation Passed` heading + validation summary; retained items → `### ⚠️ Manual Verification Required` heading + quote + item list; none → `### ✅ No Manual Verification Needed` heading + a single line `No items in this change require manual confirmation.` (no ⚠️, no list).
|
|
76
82
|
|
|
77
83
|
## Comment Lookup And Update
|
|
78
84
|
|
|
@@ -111,6 +117,15 @@ EOF
|
|
|
111
117
|
| `gh api` GET/PATCH/POST fails | warn and continue; whether the current skill should block is decided by the caller |
|
|
112
118
|
| `pr_number` points to a missing PR | warn with `PR #{pr-number} not found` and continue |
|
|
113
119
|
|
|
120
|
+
When the caller has a `{task-id}` / task directory and GET/PATCH/POST fails, record a Workflow Warning:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
node .agents/scripts/workflow-warnings.js add .agents/workspace/active/{task-id} \
|
|
124
|
+
--step pr-sync --severity ACTION_REQUIRED --code COMMENT_SYNC_FAILED \
|
|
125
|
+
--target "pr-summary" --message "{reason}" \
|
|
126
|
+
--action "Fix GitHub API / network issues and rerun the workflow step that triggers PR summary sync"
|
|
127
|
+
```
|
|
128
|
+
|
|
114
129
|
## Result Reporting
|
|
115
130
|
|
|
116
131
|
Return one of these normalized results so callers can reuse it in Activity Log entries or user output:
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
当前调用方:
|
|
8
8
|
- `create-pr`:首次创建或更新 PR 摘要评论
|
|
9
9
|
- `commit`:在已有 PR 上按需刷新摘要评论
|
|
10
|
+
- `complete-manual-validation`:人工验证完成后原地更新摘要评论,并写入可被后续聚合复用的验证产物
|
|
10
11
|
|
|
11
12
|
如果后续 skill 也需要刷新 PR 摘要,先在对应 skill 的 reference 中引用本 rule,再补充该 skill 自身的触发条件和失败语义。
|
|
12
13
|
|
|
@@ -26,17 +27,22 @@
|
|
|
26
27
|
- `plan.md` 或最新 `plan-r{N}.md`
|
|
27
28
|
- `code.md` 或最新 `code-r{N}.md`
|
|
28
29
|
- `review-code.md` 或最新 `review-code-r{N}.md`
|
|
30
|
+
- `manual-validation.md` 或最新 `manual-validation-r{N}.md`
|
|
29
31
|
|
|
30
32
|
聚合规则:
|
|
31
33
|
- 从 `plan*` 提取 2-4 条自包含的关键技术决策
|
|
32
34
|
- 用 `review-code*` 与 `code*` 构建审查历程表
|
|
33
35
|
- 从 `code*` 提取测试结果摘要
|
|
36
|
+
- 如果最新 `manual-validation*` 产物结论为通过,需人工校验段落优先渲染为 `### ✅ 人工验证已通过`,并摘要验证时间、验证范围和验证说明
|
|
34
37
|
- 某一类产物缺失时,按“无该阶段数据”处理并继续生成
|
|
35
38
|
- 需人工校验段落:只收进入 code 阶段后仍需人实际执行或判断、AI 无法自行关闭的校验点。
|
|
36
39
|
- **准入边界**:校验结论依赖真实环境、权限、账号、外部系统或人工判断,且无法通过 agent 重跑测试、补充检查或继续修复自行关闭。
|
|
37
40
|
- **来源**:`review-code*` 的「人工校验项」,以及 `code*` 中满足上述边界的校验点。
|
|
38
41
|
- **写法**:每条保留项至少写明「校验什么 + 定位(文件/改动/范围)+ 为什么只能由人校验」。
|
|
39
|
-
-
|
|
42
|
+
- **渲染优先级**:
|
|
43
|
+
1. 最新 `manual-validation*` 产物结论为通过 -> 标题 `### ✅ 人工验证已通过` + 验证摘要。
|
|
44
|
+
2. 没有通过产物且有保留项 -> 标题 `### ⚠️ 需人工校验` + 条目列表。
|
|
45
|
+
3. 没有通过产物且无保留项 -> 标题 `### ✅ 无需人工校验`,正文一行 `本次改动无需人工确认事项。`,不带条目列表。
|
|
40
46
|
|
|
41
47
|
## 评论体模板
|
|
42
48
|
|
|
@@ -72,7 +78,7 @@
|
|
|
72
78
|
*由 {agent} 自动生成 · 内部追踪:{task-id}*
|
|
73
79
|
```
|
|
74
80
|
|
|
75
|
-
> `{manual-validation-section}`
|
|
81
|
+
> `{manual-validation-section}` 按上文「需人工校验段落」聚合规则渲染:已通过产物 → `### ✅ 人工验证已通过` 标题 + 验证摘要;有保留项 → `### ⚠️ 需人工校验` 标题 + 引用说明 + 条目列表;无保留项 → `### ✅ 无需人工校验` 标题 + 一行 `本次改动无需人工确认事项。`(不带 ⚠️、不带列表)。
|
|
76
82
|
|
|
77
83
|
## 评论查找与更新
|
|
78
84
|
|
|
@@ -111,6 +117,15 @@ EOF
|
|
|
111
117
|
| `gh api` GET/PATCH/POST 失败 | 输出警告并继续;是否阻塞当前 skill 由调用方决定 |
|
|
112
118
|
| `pr_number` 指向的 PR 不存在 | 输出 `PR #{pr-number} not found` 警告并继续 |
|
|
113
119
|
|
|
120
|
+
当调用方存在 `{task-id}` / task 目录且 GET/PATCH/POST 失败时,记录 Workflow Warning:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
node .agents/scripts/workflow-warnings.js add .agents/workspace/active/{task-id} \
|
|
124
|
+
--step pr-sync --severity ACTION_REQUIRED --code COMMENT_SYNC_FAILED \
|
|
125
|
+
--target "pr-summary" --message "{reason}" \
|
|
126
|
+
--action "修复 GitHub API / 网络问题后重跑触发 PR 摘要同步的 workflow 步骤"
|
|
127
|
+
```
|
|
128
|
+
|
|
114
129
|
## 结果回传
|
|
115
130
|
|
|
116
131
|
统一回传以下结果之一,供调用方在 Activity Log 或用户输出中复用:
|
|
@@ -52,7 +52,7 @@ Map user intent to the corresponding workflow command:
|
|
|
52
52
|
|
|
53
53
|
**Pairing and rendering** (`ai task log`): a started entry pairs with the next same-`{base}` done entry onto one row (repeated executions of the same base pair FIFO by ascending time). The STARTED column shows the start time, DONE the completion time; started with no done = in progress (DONE shows `(in progress)`); done with no started (legacy logs) = a standalone completed row. All three shapes are valid and never error.
|
|
54
54
|
|
|
55
|
-
**Human counts** (`ai task log`): review step rows fold two human counts into the NOTE verdict text, comma-separated, right after `blockers/major/minor`, with fixed English labels `Manual-validation: {e}, Human-decision: {h}`. `Human-decision` (`{h}`) maps canonical step prefixes (`Review Analysis` / `Review Plan` / `Review Code`) to `analysis` / `plan` / `code`, then counts current rows in `## Review Disagreement Ledger` / `## 审查分歧账本` for that stage with `status
|
|
55
|
+
**Human counts** (`ai task log`): review step rows fold two human counts into the NOTE verdict text, comma-separated, right after `blockers/major/minor`, with fixed English labels `Manual-validation: {e}, Human-decision: {h}`. `Human-decision` (`{h}`) maps canonical step prefixes (`Review Analysis` / `Review Plan` / `Review Code`) to `analysis` / `plan` / `code`, then counts current pending rows in `## Review Disagreement Ledger` / `## 审查分歧账本` for that stage with `status = needs-human-decision`, excluding rows already marked `human-decided`. `Manual-validation` (`{e}`) parses the `Manual-validation: {n}` source field from the review done note (normalized to the same field when displayed), defaulting to `0`. Non-review rows carry no human counts.
|
|
56
56
|
|
|
57
57
|
**Gate** (`checkActivityLog`): when computing the "latest action / freshness" it skips `[started]` lines (ascending-order and format checks still cover every line), so a started marker never satisfies a skill's `expected_action_pattern`.
|
|
58
58
|
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
**配对与渲染**(`ai task log`):按 `{基名}` 把 started 与其后最近的同名 done 配成一行(同基名多次执行按时间升序 FIFO 配对)。STARTED 列显示 started 时间、DONE 列显示 done 时间;只有 started 无 done = 进行中(DONE 显示 `(in progress)`);只有 done 无 started(历史日志)= 单态完成行。三种形态都合法、不报错。
|
|
58
58
|
|
|
59
|
-
**人工计数**(`ai task log`):审查步骤行把两项人工计数并入 NOTE 的 verdict 文本,逗号分隔、紧随 `blockers/major/minor`,固定英文标签 `Manual-validation: {e}, Human-decision: {h}`。`Human-decision`(`{h}`)按 canonical 步骤名前缀(`Review Analysis` / `Review Plan` / `Review Code`)映射到 `analysis` / `plan` / `code`,统计 `## 审查分歧账本` 中对应阶段 `status
|
|
59
|
+
**人工计数**(`ai task log`):审查步骤行把两项人工计数并入 NOTE 的 verdict 文本,逗号分隔、紧随 `blockers/major/minor`,固定英文标签 `Manual-validation: {e}, Human-decision: {h}`。`Human-decision`(`{h}`)按 canonical 步骤名前缀(`Review Analysis` / `Review Plan` / `Review Code`)映射到 `analysis` / `plan` / `code`,统计 `## 审查分歧账本` 中对应阶段 `status = needs-human-decision` 的当前待裁决数量,不包含已 `human-decided` 的行;`Manual-validation`(`{e}`)解析 review done note 中的 `Manual-validation: {n}` 源字段(展示时归一化为同一字段),缺失为 `0`。非审查步骤不附加人工计数。
|
|
60
60
|
|
|
61
61
|
**gate**(`checkActivityLog`):计算「最新 action / freshness」时跳过 `[started]` 行(升序与格式校验仍覆盖全部行),故 started 标记不会污染各 SKILL 的 `expected_action_pattern`。
|
|
62
62
|
|
|
@@ -112,6 +112,7 @@ export function check({ taskDir, config, artifactFile }, shared) {
|
|
|
112
112
|
checkCommentMarker,
|
|
113
113
|
checkPrCommentMarker,
|
|
114
114
|
checkPrCommentLastCommit,
|
|
115
|
+
checkPrCommentRequiredPatterns,
|
|
115
116
|
checkCommentContent,
|
|
116
117
|
checkTaskCommentContent,
|
|
117
118
|
checkInLabelsComputed,
|
|
@@ -158,6 +159,7 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
|
158
159
|
return { earlyReturn: blockedResult(CHECK_TYPE, upstreamRepo.message, "network_error") };
|
|
159
160
|
}
|
|
160
161
|
const permissions = detectPermissions(upstreamRepo.value, taskDir);
|
|
162
|
+
const repoOwnerType = detectRepoOwnerType(upstreamRepo.value, taskDir);
|
|
161
163
|
const expectedValues = resolveExpectedValues(config);
|
|
162
164
|
if (!expectedValues.ok) {
|
|
163
165
|
return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
@@ -180,6 +182,7 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
|
180
182
|
issueNumber,
|
|
181
183
|
prNumber,
|
|
182
184
|
upstreamRepo: upstreamRepo.value,
|
|
185
|
+
repoOwnerType,
|
|
183
186
|
hasTriage: permissions.hasTriage,
|
|
184
187
|
hasPush: permissions.hasPush,
|
|
185
188
|
expectedStatusLabel: expectedValues.statusLabel,
|
|
@@ -553,6 +556,41 @@ function checkPrCommentLastCommit(context, remoteData) {
|
|
|
553
556
|
);
|
|
554
557
|
}
|
|
555
558
|
|
|
559
|
+
function checkPrCommentRequiredPatterns(context, remoteData) {
|
|
560
|
+
const patterns = context.config.expected_pr_comment_required_patterns || [];
|
|
561
|
+
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!context.prMarker) {
|
|
566
|
+
return failResult(CHECK_TYPE,
|
|
567
|
+
"expected_pr_comment_required_patterns requires expected_pr_comment_marker",
|
|
568
|
+
"check_failed"
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
573
|
+
if (!comment) {
|
|
574
|
+
return failResult(CHECK_TYPE,
|
|
575
|
+
`Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
|
|
576
|
+
"check_failed"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const body = String(comment.body || "");
|
|
581
|
+
for (const pattern of patterns) {
|
|
582
|
+
const regex = new RegExp(pattern, "m");
|
|
583
|
+
if (!regex.test(body)) {
|
|
584
|
+
return failResult(CHECK_TYPE,
|
|
585
|
+
`PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`,
|
|
586
|
+
"check_failed"
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
|
|
556
594
|
function checkCommentContent(context, remoteData) {
|
|
557
595
|
if (!context.config.verify_comment_content) {
|
|
558
596
|
return null;
|
|
@@ -722,6 +760,10 @@ function checkIssueType(context, remoteData) {
|
|
|
722
760
|
}
|
|
723
761
|
|
|
724
762
|
if (!remoteData.issueType) {
|
|
763
|
+
if (context.repoOwnerType === "User") {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
|
|
725
767
|
return failResult(CHECK_TYPE,
|
|
726
768
|
`Issue #${context.issueNumber} has no Issue Type set`,
|
|
727
769
|
"check_failed"
|
|
@@ -1228,6 +1270,21 @@ function detectPermissions(upstreamRepo, taskDir) {
|
|
|
1228
1270
|
};
|
|
1229
1271
|
}
|
|
1230
1272
|
|
|
1273
|
+
function detectRepoOwnerType(upstreamRepo, taskDir) {
|
|
1274
|
+
const ownerTypeResult = withRetry(() => ghText([
|
|
1275
|
+
"api",
|
|
1276
|
+
`repos/${upstreamRepo}`,
|
|
1277
|
+
"--jq",
|
|
1278
|
+
".owner.type // empty"
|
|
1279
|
+
], taskDir));
|
|
1280
|
+
|
|
1281
|
+
if (!ownerTypeResult.ok) {
|
|
1282
|
+
return "unknown";
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
return ownerTypeResult.value || "unknown";
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1231
1288
|
function ghJson(args, cwd) {
|
|
1232
1289
|
const result = ghCommand(args, cwd);
|
|
1233
1290
|
if (!result.ok) {
|
|
@@ -61,6 +61,10 @@ const LEDGER_TERMINAL_OK = new Set(["confirmed", "closed", "human-decided"]);
|
|
|
61
61
|
const DEFAULT_MAX_HANDSHAKE_ROUNDS = 3;
|
|
62
62
|
const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
|
|
63
63
|
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
64
|
+
const WORKFLOW_WARNING_SECTION_NAMES = ["工作流告警", "Workflow Warnings"];
|
|
65
|
+
const WORKFLOW_WARNING_STATUSES = new Set(["open", "resolved", "ignored"]);
|
|
66
|
+
const WORKFLOW_WARNING_SEVERITIES = new Set(["IMPORTANT", "ACTION_REQUIRED"]);
|
|
67
|
+
const WORKFLOW_WARNING_ID_PATTERN = /^WW-\d+$/;
|
|
64
68
|
|
|
65
69
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
66
70
|
const repoRoot = path.resolve(path.dirname(scriptPath), "..", "..");
|
|
@@ -284,6 +288,11 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
284
288
|
return failResult("task-meta", branchValidationError);
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
const warningValidationErrors = validateWorkflowWarnings(task.content);
|
|
292
|
+
if (warningValidationErrors.length > 0) {
|
|
293
|
+
return failResult("task-meta", `Invalid Workflow Warnings: ${warningValidationErrors.join("; ")}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
287
296
|
const expectedStep = config.expected_step;
|
|
288
297
|
if (expectedStep && metadata.current_step !== expectedStep) {
|
|
289
298
|
return failResult(
|
|
@@ -561,6 +570,117 @@ function parseLedgerRows(section) {
|
|
|
561
570
|
return rows;
|
|
562
571
|
}
|
|
563
572
|
|
|
573
|
+
function splitMarkdownTableRow(line) {
|
|
574
|
+
let value = String(line || "").trim();
|
|
575
|
+
if (!value.startsWith("|")) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
value = value.replace(/^\|/, "").replace(/\|$/, "");
|
|
579
|
+
|
|
580
|
+
const cells = [];
|
|
581
|
+
let cell = "";
|
|
582
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
583
|
+
const char = value[index];
|
|
584
|
+
if (char === "|" && !isEscapedAt(value, index)) {
|
|
585
|
+
cells.push(unescapeMarkdownTableCell(cell.trim()));
|
|
586
|
+
cell = "";
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
cell += char;
|
|
590
|
+
}
|
|
591
|
+
cells.push(unescapeMarkdownTableCell(cell.trim()));
|
|
592
|
+
return cells;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function unescapeMarkdownTableCell(value) {
|
|
596
|
+
let output = "";
|
|
597
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
598
|
+
const char = value[index];
|
|
599
|
+
const next = value[index + 1];
|
|
600
|
+
if (char === "\\" && (next === "\\" || next === "|")) {
|
|
601
|
+
output += next;
|
|
602
|
+
index += 1;
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
output += char;
|
|
606
|
+
}
|
|
607
|
+
return output;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function isEscapedAt(value, index) {
|
|
611
|
+
let backslashes = 0;
|
|
612
|
+
for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
|
|
613
|
+
backslashes += 1;
|
|
614
|
+
}
|
|
615
|
+
return backslashes % 2 === 1;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function parseWorkflowWarningRows(section) {
|
|
619
|
+
const rows = [];
|
|
620
|
+
for (const rawLine of String(section || "").split(/\r?\n/)) {
|
|
621
|
+
const cells = splitMarkdownTableRow(rawLine);
|
|
622
|
+
if (cells.length === 0) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if ((cells[0] || "").toLowerCase() === "id") {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (cells.every((cell) => /^:?-{3,}:?$/.test(cell))) {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
rows.push(cells);
|
|
632
|
+
}
|
|
633
|
+
return rows;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function validateWorkflowWarnings(content) {
|
|
637
|
+
const section = getSectionContent(content, WORKFLOW_WARNING_SECTION_NAMES);
|
|
638
|
+
if (!section.trim()) {
|
|
639
|
+
return [];
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const rows = parseWorkflowWarningRows(section);
|
|
643
|
+
const errors = [];
|
|
644
|
+
for (const cells of rows) {
|
|
645
|
+
if (cells.length < 11) {
|
|
646
|
+
errors.push(`malformed row (expected 11 columns): ${cells.join(" | ")}`);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const [id, time, step, severity, code, status, target, message, action, resolvedAt, resolution] = cells;
|
|
650
|
+
if (!WORKFLOW_WARNING_ID_PATTERN.test(id)) {
|
|
651
|
+
errors.push(`${id || "(empty id)"}: invalid id`);
|
|
652
|
+
}
|
|
653
|
+
if (!DATE_TIME_PATTERN.test(time)) {
|
|
654
|
+
errors.push(`${id}: invalid time '${time}'`);
|
|
655
|
+
}
|
|
656
|
+
if (isBlank(step)) {
|
|
657
|
+
errors.push(`${id}: step is required`);
|
|
658
|
+
}
|
|
659
|
+
if (!WORKFLOW_WARNING_SEVERITIES.has(severity)) {
|
|
660
|
+
errors.push(`${id}: illegal severity '${severity}'`);
|
|
661
|
+
}
|
|
662
|
+
if (isBlank(code)) {
|
|
663
|
+
errors.push(`${id}: code is required`);
|
|
664
|
+
}
|
|
665
|
+
if (!WORKFLOW_WARNING_STATUSES.has(status)) {
|
|
666
|
+
errors.push(`${id}: illegal status '${status}'`);
|
|
667
|
+
}
|
|
668
|
+
if (isBlank(target)) {
|
|
669
|
+
errors.push(`${id}: target is required`);
|
|
670
|
+
}
|
|
671
|
+
if (isBlank(message)) {
|
|
672
|
+
errors.push(`${id}: message is required`);
|
|
673
|
+
}
|
|
674
|
+
if (status === "open" && isBlank(action)) {
|
|
675
|
+
errors.push(`${id}: open warning requires action`);
|
|
676
|
+
}
|
|
677
|
+
if ((status === "resolved" || status === "ignored") && (isBlank(resolvedAt) || isBlank(resolution))) {
|
|
678
|
+
errors.push(`${id}: ${status} warning requires resolved_at and resolution`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return errors;
|
|
682
|
+
}
|
|
683
|
+
|
|
564
684
|
function resolveReviewSetting(config, key, fallback) {
|
|
565
685
|
if (config && config[key] !== undefined && config[key] !== null) {
|
|
566
686
|
return config[key];
|