@fitlab-ai/agent-infra 0.7.1 → 0.7.2

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.
Files changed (137) hide show
  1. package/bin/cli.ts +11 -0
  2. package/dist/bin/cli.js +12 -0
  3. package/dist/lib/sandbox/commands/create.js +10 -2
  4. package/dist/lib/sandbox/commands/enter.js +8 -7
  5. package/dist/lib/sandbox/commands/list-running.js +21 -32
  6. package/dist/lib/sandbox/commands/ls.js +20 -22
  7. package/dist/lib/sandbox/index.js +7 -3
  8. package/dist/lib/sandbox/task-resolver.js +1 -1
  9. package/dist/lib/sandbox/tools.js +1 -1
  10. package/dist/lib/table.js +29 -0
  11. package/dist/lib/task/commands/ls.js +122 -0
  12. package/dist/lib/task/commands/show.js +135 -0
  13. package/dist/lib/task/frontmatter.js +32 -0
  14. package/dist/lib/task/index.js +41 -0
  15. package/dist/lib/task/short-id.js +80 -0
  16. package/lib/sandbox/commands/create.ts +11 -2
  17. package/lib/sandbox/commands/enter.ts +8 -7
  18. package/lib/sandbox/commands/list-running.ts +23 -37
  19. package/lib/sandbox/commands/ls.ts +25 -25
  20. package/lib/sandbox/index.ts +7 -3
  21. package/lib/sandbox/task-resolver.ts +1 -1
  22. package/lib/sandbox/tools.ts +1 -1
  23. package/lib/table.ts +32 -0
  24. package/lib/task/commands/ls.ts +138 -0
  25. package/lib/task/commands/show.ts +139 -0
  26. package/lib/task/frontmatter.ts +30 -0
  27. package/lib/task/index.ts +44 -0
  28. package/lib/task/short-id.ts +97 -0
  29. package/package.json +1 -1
  30. package/templates/.agents/hooks/auto-resume.sh +87 -0
  31. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  32. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  33. package/templates/.agents/rules/milestone-inference.github.en.md +4 -1
  34. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -1
  35. package/templates/.agents/rules/next-step-output.en.md +59 -0
  36. package/templates/.agents/rules/next-step-output.zh-CN.md +59 -0
  37. package/templates/.agents/rules/task-short-id.en.md +54 -62
  38. package/templates/.agents/rules/task-short-id.zh-CN.md +35 -54
  39. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +17 -0
  40. package/templates/.agents/scripts/task-short-id.js +32 -189
  41. package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -12
  42. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -12
  43. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  44. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  45. package/templates/.agents/skills/block-task/SKILL.en.md +6 -6
  46. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +6 -6
  47. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  48. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
  49. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
  50. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  51. package/templates/.agents/skills/check-task/SKILL.en.md +12 -10
  52. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +12 -10
  53. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -6
  54. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -6
  55. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -6
  56. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -6
  57. package/templates/.agents/skills/code-task/SKILL.en.md +10 -6
  58. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +11 -6
  59. package/templates/.agents/skills/code-task/config/verify.en.json +2 -1
  60. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +2 -1
  61. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +10 -5
  62. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +10 -5
  63. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  64. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  65. package/templates/.agents/skills/code-task/reference/report-template.en.md +8 -0
  66. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +8 -0
  67. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  68. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  69. package/templates/.agents/skills/commit/reference/task-status-update.en.md +9 -9
  70. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +9 -9
  71. package/templates/.agents/skills/complete-task/SKILL.en.md +6 -2
  72. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -2
  73. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -1
  74. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -1
  75. package/templates/.agents/skills/create-pr/SKILL.en.md +6 -6
  76. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -6
  77. package/templates/.agents/skills/create-pr/config/verify.json +2 -1
  78. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  79. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  80. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  81. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  82. package/templates/.agents/skills/create-task/SKILL.en.md +17 -17
  83. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +17 -17
  84. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  85. package/templates/.agents/skills/import-codescan/SKILL.en.md +8 -8
  86. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +8 -8
  87. package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
  88. package/templates/.agents/skills/import-dependabot/SKILL.en.md +8 -8
  89. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +8 -8
  90. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
  91. package/templates/.agents/skills/import-issue/SKILL.en.md +7 -7
  92. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +7 -7
  93. package/templates/.agents/skills/plan-task/SKILL.en.md +10 -12
  94. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -12
  95. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  96. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  97. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  98. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  99. package/templates/.agents/skills/review-analysis/SKILL.en.md +4 -2
  100. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +4 -2
  101. package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -2
  102. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -2
  103. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +15 -15
  104. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +15 -15
  105. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -1
  106. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +7 -1
  107. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +2 -0
  108. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +2 -0
  109. package/templates/.agents/skills/review-code/SKILL.en.md +5 -2
  110. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -2
  111. package/templates/.agents/skills/review-code/config/verify.en.json +3 -2
  112. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -2
  113. package/templates/.agents/skills/review-code/reference/output-templates.en.md +9 -9
  114. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +9 -9
  115. package/templates/.agents/skills/review-code/reference/report-template.en.md +7 -1
  116. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +7 -1
  117. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -0
  118. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -0
  119. package/templates/.agents/skills/review-plan/SKILL.en.md +4 -2
  120. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +4 -2
  121. package/templates/.agents/skills/review-plan/config/verify.en.json +3 -2
  122. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -2
  123. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +15 -15
  124. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +15 -15
  125. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -1
  126. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -1
  127. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +2 -0
  128. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +2 -0
  129. package/templates/.agents/templates/task.en.md +0 -1
  130. package/templates/.agents/templates/task.zh-CN.md +0 -1
  131. package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
  132. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +1 -1
  133. package/templates/.agents/workflows/feature-development.en.yaml +1 -1
  134. package/templates/.agents/workflows/feature-development.zh-CN.yaml +1 -1
  135. package/templates/.agents/workflows/refactoring.en.yaml +1 -1
  136. package/templates/.agents/workflows/refactoring.zh-CN.yaml +1 -1
  137. package/templates/.claude/settings.json +11 -0
@@ -1,25 +1,27 @@
1
1
  # 任务短号
2
2
 
3
- 短号让所有 SKILL 在 active 任务生命周期内可以用 `#NN` 替代完整的 22 字符
4
- `TASK-YYYYMMDD-HHMMSS`。
3
+ 短号让所有 SKILL 在 active 任务生命周期内可以用 `#NN` 或裸数字 `N`(推荐)替代
4
+ 完整的 22 字符 `TASK-YYYYMMDD-HHMMSS`。
5
5
 
6
6
  ## 语法
7
7
 
8
- - 格式:`^#\d{shortIdLength}$`(**零填充到固定宽度**;默认 `shortIdLength=2` 时
9
- 形如 `#01`、`#07`、`#42`)。
10
- - **必须**零填充到 `shortIdLength` 位(默认 2 位:`#1` 视为格式错误,应输入
11
- `#01`)。这是为了视觉对齐与盲打体验。
8
+ - 字面接受两种等价形式:
9
+ - **裸数字 `N`**(推荐,无需 shell 引号):如 `1`、`7`、`42`。
10
+ - **`#`-前缀 `#N` / `#NN`**(也接受;但 bash 需 `'...'` 引号):如 `#1`、`#01`、`#42`。
11
+ - 解析规则:去前导零后取数值 `n`,若 `n == 0` 报错(保留);若 `n > 10^shortIdLength - 1`
12
+ 报错(超容量);否则归一化为 `#${n.padStart(shortIdLength, '0')}`,作为注册表 key。
13
+ - 默认 `shortIdLength=2` 时容量 `n ∈ [1, 99]`,注册表 key 形如 `01`、`07`、`42`。
12
14
  - `#00`(或 `shortIdLength=1` 时 `#0`)保留、永不分配;纯数字、不引入字母。
13
- - 完整 `TASK-…` 入参在所有路径下行为与现状等价;`#NN` 只是别名,不是持久化任务 ID。
15
+ - 完整 `TASK-…` 入参在所有路径下行为与现状等价;`#NN` / 裸数字只是别名,不是持久化任务 ID。
14
16
 
15
17
  ## 生命周期
16
18
 
17
- | 动作 | 触发时机 | 注册表 / task.md 效应 |
19
+ | 动作 | 触发时机 | 注册表效应 |
18
20
  |-----------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------|
19
- | alloc | `create-task`、`import-issue`、`import-codescan`、`import-dependabot` | 分配最小可用 `#NN`,写入 task.md 的 `short_id` 字段。 |
21
+ | alloc | `create-task`、`import-issue`、`import-codescan`、`import-dependabot` | 分配最小可用 `#NN`,写入注册表。 |
20
22
  | resolve | 生命周期 SKILL(`analyze-task` / `plan-task` / `code-task` / `review-*` / `commit` / …) | `#NN` → 完整 task id 查询,不分配。 |
21
- | release | `complete-task`、`cancel-task`、`block-task`、`close-codescan`、`close-dependabot` | 从注册表移除;task.md 的 `short_id` 字段保留作为历史值。 |
22
- | re-alloc | `restore-task` | 重新分配(可能与历史不同),写入注册表与 task.md。 |
23
+ | release | `complete-task`、`cancel-task`、`block-task`、`close-codescan`、`close-dependabot` | 从注册表移除。 |
24
+ | re-alloc | `restore-task` | 重新分配(可能与历史不同),写入注册表。 |
23
25
 
24
26
  短号仅在任务处于 `.agents/workspace/active/` 期间有效;任务移动到
25
27
  `completed/` / `blocked/` / `archive/` 后短号立即释放,可被新任务复用。
@@ -44,21 +46,20 @@
44
46
  | 入口 | 注册表命中 | 注册表未命中 |
45
47
  |-----------------------------------------------------------|----------------------|--------------------------------------------------------|
46
48
  | SKILL 入参解析器(生命周期 SKILL) | 解析为完整 task id | **严格报错** —— 短号不存在 / 格式错误 |
47
- | `ai sandbox enter '#NN'` / `ai sandbox exec '#NN' …` | 解析为完整 task id | 回退到 running sandbox ls 行号语义(保留 #414 行为)|
49
+ | `ai sandbox exec <N \| '#N'>` / `ai sandbox create <N \| '#N'>` | 解析为完整 task id 后查 task.md 取 `branch` | **严格报错** —— 不再回退到 ls 行号或字面分支名;提示用任务短号 / `TASK-id` / 分支名 |
48
50
 
49
- `list --verify` 严格只读:报告 active 目录 / 注册表 / 各 task.md 的 `short_id`
50
- 三者差异,但不修改任何状态。
51
+ `list --verify` 严格只读:报告 active 目录 / 注册表 两者差异,但不修改任何状态。
51
52
 
52
53
  ## SKILL 入参解析
53
54
 
54
55
  任意 SKILL(含 alloc / resolve / release / re-alloc 四类生命周期入口)在收到
55
56
  `{task-id}` 入参后,必须按以下契约处理:
56
57
 
57
- 1. 如果 `{task-id}` 字面以 `#` 开头:
58
+ 1. 如果 `{task-id}` 字面匹配 `^[#]?[0-9]+$`(裸数字 `N` 或 `#`-前缀 `#N`):
58
59
 
59
60
  ```bash
60
- if [[ "{task-id}" == "#"* ]]; then
61
- # 脚本本身已输出完整错误(含「expected #NN (N-digit zero-padded; e.g. '#01')」);
61
+ if [[ "{task-id}" =~ ^[#]?[0-9]+$ ]]; then
62
+ # 脚本本身已输出完整错误(含 reserved / exceeds shortIdLength capacity 等场景);
62
63
  # 调用方只需透传退出码
63
64
  task_id=$(node .agents/scripts/task-short-id.js resolve "{task-id}") || exit 1
64
65
  else
@@ -71,54 +72,34 @@ fi
71
72
 
72
73
  ## 存储位置
73
74
 
74
- 短号系统跨两处持久化状态,二者在稳态时一一对应:
75
-
76
- | 位置 | 写入时机 | 读取时机 | 删除时机 |
77
- |---|---|---|---|
78
- | `.agents/workspace/active/.short-ids.json`(注册表) | `alloc` / 冷启动迁移 | `resolve` 唯一权威源 / `list` / `list --verify` | `release` / 冷启动 stale 清理 |
79
- | 各 task.md frontmatter 的 `short_id` 字段 | `alloc` / 冷启动迁移 | `list --verify`(比对一致性) | **永不删除**(归档后保留为历史值) |
80
-
81
- **注册表**:
75
+ 短号是纯本地状态,唯一持久化在注册表 `.agents/workspace/active/.short-ids.json`,task.md 不持有短号:
82
76
 
83
77
  - 路径:`<repo-root>/.agents/workspace/active/.short-ids.json`
84
78
  - Schema:`{ "version": 1, "ids": { "01": "TASK-20260609-192644", "02": "TASK-…" } }`
85
79
  - key 是零填充到 `task.shortIdLength` 位的字符串,value 是完整 `TASK-…` task id
86
80
  - 自动 git ignore(active 工作区整体 ignore;无需新增 ignore 条目)
87
- - 首次 `alloc` / `resolve` 时按需自动创建;不存在时按空注册表处理
88
-
89
- **task.md `short_id` 字段**:
81
+ - 首次 `alloc` 时按需自动创建;不存在时按空注册表处理
82
+ - 短号只由显式 `alloc`(`create-task` / `import-*` / `restore-task`)分配;`resolve` / `list` / `release` 不分配,仅在执行时自动清理指向非 active 任务的 stale entry
83
+ - 归档(complete-task / cancel-task / block-task / close-*)后注册表 entry 立即删除,短号可被新任务复用;归档后引用任务一律用完整 `TASK-…` id
90
84
 
91
- - frontmatter 中、紧跟 `id` 字段之后;格式 `short_id: #01`
92
- - 与注册表 key 字面一致(含 `#` 前缀)
93
- - 归档(complete-task / cancel-task / block-task / close-*)后:注册表 entry 立即
94
- 删除(短号可被新任务复用),但 task.md `short_id` 字段保留作为历史值。解析器
95
- 只信任注册表
96
- - 冷启动迁移:升级 agent-infra 后首次 alloc / resolve 路径会扫描所有 active
97
- 目录并为缺字段的 task.md 补发短号;补发受字段保护约束(不刷新
98
- `updated_at` / `agent_infra_version`、不追加 Activity Log)
99
-
100
- `resolve('#NN')` 工作流:① 校验入参严格匹配 `^#\d{shortIdLength}$` → ② 直接以
101
- `NN` 作为 key 查注册表 `ids` → ③ 命中返回完整 task id,未命中按 `list --verify`
102
- 给出修复指引退出 1。
85
+ `resolve(<N|'#N'>)` 工作流:① 校验入参匹配 `^[#]?[0-9]+$` 去前导零取
86
+ 数值 `n`,按 `n` 是否 `== 0`(保留)/ `> 10^shortIdLength - 1`(超容量)/
87
+ 正常 三类处理 正常时以 `n.padStart(shortIdLength, '0')` 作为 key
88
+ 注册表 `ids` → ④ 命中返回完整 task id,未命中按 `list --verify` 给出修复指引退出 1。
103
89
 
104
90
  ## 错误场景
105
91
 
106
- - **短号不存在**:注册表中无 `#NN`。可能是任务已归档(短号已释放)或输入错误。
92
+ - **短号不存在**:注册表中无对应 key。可能是任务已归档(短号已释放)或输入错误。退出码 1。
107
93
  - **注册表损坏**(同一 taskId 出现多次或 JSON 无法解析):退出码 2,需人工处理。
108
- - **参数格式错误**(如 `#00`、`#abc`、`#`、`#1` `shortIdLength=2` 时):退出码 1。
94
+ - **保留键**:解析后 `n == 0`(输入如 `0`、`#0`、`#00`)。退出码 1。
95
+ - **超容量**:解析后 `n > 10^shortIdLength - 1`(如 `shortIdLength=2` 下 `100` 或 `#100`)。退出码 1。
96
+ - **参数格式错误**:入参既不是 `^[#]?[0-9]+$` 也不是 `TASK-id`(如 `#abc`、`#`、`5.5`)。退出码 1。
109
97
 
110
98
  ## 跨 TUI 引号要求
111
99
 
112
- bash `#` 是注释起始符,必须单引号:`ai sandbox exec '#03' 'npm test'`。
113
- Claude Code / Codex / Gemini CLI / OpenCode 在加引号时都能把 `#NN` 字面传递到
114
- SKILL 的 `ARGUMENTS`。
115
-
116
- ## 冷启动迁移
117
-
118
- 升级 agent-infra 后,首次 `alloc` / `resolve` 调用会触发冷启动迁移:
100
+ 裸数字 `N` 在所有 shell TUI 中都安全无需引号,推荐写法:
101
+ `ai sandbox exec 11 'npm test'`、`/review-analysis 11`。
119
102
 
120
- - 所有 active task.md `short_id` 字段时自动补发并回写(仅修改 `short_id`
121
- 一行,不刷新 `updated_at` / `agent_infra_version`,不追加 Activity Log)。
122
- - active 任务总数超过 `shortIdLength` 容量,**在任何写入之前**报错退出 2。
123
- - 若 task.md 写入中途失败,`tx.commit()` 按缓存的原内容回滚所有已写文件(含
124
- `mtime` / `atime` 恢复)。
103
+ `#N` / `#NN` 写法也接受;但 bash `#` 是注释起始符,必须单引号:
104
+ `ai sandbox exec '#03' 'npm test'`。Claude Code / Codex / Gemini CLI / OpenCode
105
+ 在加引号时都能把 `#NN` 字面传递到 SKILL `ARGUMENTS`。
@@ -5,6 +5,7 @@ import spawn from "cross-spawn";
5
5
 
6
6
  const CHECK_TYPE = "platform-sync";
7
7
  const DEFAULT_RETRY_DELAYS_MS = [3000, 10000];
8
+ const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
8
9
  const FRONTMATTER_FIELD_MAP = {
9
10
  priority: "Priority",
10
11
  effort: "Effort",
@@ -811,6 +812,22 @@ function checkMilestone(context, remoteData) {
811
812
  );
812
813
  }
813
814
 
815
+ if (context.config.verify_milestone_specific) {
816
+ const issueTitle = remoteData.issue.milestone.title;
817
+ if (VERSION_LINE_REGEX.test(issueTitle)) {
818
+ return failResult(CHECK_TYPE,
819
+ `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`,
820
+ "check_failed"
821
+ );
822
+ }
823
+ if (context.prNumber && remoteData.prMilestone?.title && VERSION_LINE_REGEX.test(remoteData.prMilestone.title)) {
824
+ return failResult(CHECK_TYPE,
825
+ `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`,
826
+ "check_failed"
827
+ );
828
+ }
829
+ }
830
+
814
831
  return null;
815
832
  }
816
833
 
@@ -32,11 +32,11 @@ function usage() {
32
32
  "Usage: task-short-id.js <subcommand> [args]",
33
33
  "",
34
34
  "Subcommands:",
35
- " alloc <task-id> Allocate short id for a task; writes registry + short_id to task.md",
35
+ " alloc <task-id> Allocate short id for a task in the registry",
36
36
  " release <task-id> Release short id (idempotent; exit 0 if not present)",
37
37
  " resolve <#N> Resolve short id to full task id",
38
38
  " list Print registry JSON",
39
- " list --verify Read-only check; exit 1 if active dir / registry / task.md disagree",
39
+ " list --verify Read-only check; exit 1 if active dir / registry disagree",
40
40
  "",
41
41
  "Options:",
42
42
  " --active-dir <path> Override active dir (default: <repo>/.agents/workspace/active)",
@@ -161,17 +161,6 @@ function withRegistryLock(activeDir, fn, timeoutMs = DEFAULT_LOCK_TIMEOUT_MS) {
161
161
  }
162
162
  }
163
163
 
164
- function writeTaskMdShortId(taskMdPath, shortId) {
165
- const content = fs.readFileSync(taskMdPath, "utf8");
166
- let updated;
167
- if (/^short_id:.*$/m.test(content)) {
168
- updated = content.replace(/^short_id:.*$/m, `short_id: ${shortId}`);
169
- } else {
170
- updated = content.replace(/^(id:.*)$/m, `$1\nshort_id: ${shortId}`);
171
- }
172
- fs.writeFileSync(taskMdPath, updated);
173
- }
174
-
175
164
  function padShortId(n, shortIdLength) {
176
165
  return String(n).padStart(shortIdLength, "0");
177
166
  }
@@ -185,24 +174,34 @@ function allocateMinFreeInt(registry, shortIdLength) {
185
174
  }
186
175
 
187
176
  function parseShortIdArg(arg, shortIdLength) {
188
- const re = new RegExp(`^#\\d{${shortIdLength}}$`);
189
- if (!re.test(arg)) {
190
- const example =
191
- shortIdLength === 1 ? "'#1'" : shortIdLength === 2 ? "'#01'" : `'#${"0".repeat(shortIdLength - 1)}1'`;
177
+ const L = shortIdLength;
178
+ const max = Math.pow(10, L) - 1;
179
+ // Accept bare numeric or '#'-prefixed; canonicalize to zero-padded key.
180
+ // Bare numeric is the recommended form (no shell quoting needed).
181
+ const m = /^#?(\d+)$/.exec(arg);
182
+ if (!m) {
192
183
  writeStderr(
193
184
  `Error: invalid short id format '${arg}', ` +
194
- `expected #${"N".repeat(shortIdLength)} (${shortIdLength}-digit zero-padded; e.g. ${example})\n`
185
+ `expected bare digits (recommended) or '#'-prefixed digits; ` +
186
+ `e.g. '11' or '#11' (shortIdLength=${L}, max=${max})\n`
187
+ );
188
+ process.exit(1);
189
+ }
190
+ const n = Number(m[1]);
191
+ if (n === 0) {
192
+ writeStderr(
193
+ `Error: short id '${arg}' is invalid (#${"0".repeat(L)} is reserved)\n`
195
194
  );
196
195
  process.exit(1);
197
196
  }
198
- const key = arg.slice(1);
199
- if (Number(key) === 0) {
197
+ if (n > max) {
200
198
  writeStderr(
201
- `Error: short id '${arg}' is invalid (#${"0".repeat(shortIdLength)} is reserved)\n`
199
+ `Error: short id ${n} exceeds shortIdLength=${L} capacity (max=${max}); ` +
200
+ `archive tasks or raise task.shortIdLength in .agents/.airc.json\n`
202
201
  );
203
202
  process.exit(1);
204
203
  }
205
- return key;
204
+ return String(n).padStart(L, "0");
206
205
  }
207
206
 
208
207
  function planTransaction(registry, activeDir, shortIdLength) {
@@ -216,7 +215,7 @@ function planTransaction(registry, activeDir, shortIdLength) {
216
215
  .filter((d) => fs.existsSync(path.join(activeDir, d, "task.md")))
217
216
  );
218
217
 
219
- // A2: stale entries
218
+ // A2: stale entries (registry points at a task no longer active)
220
219
  const pendingRegistryDeletes = [];
221
220
  for (const [key, taskId] of Object.entries(registry.ids)) {
222
221
  if (!activeTaskIds.has(taskId)) pendingRegistryDeletes.push(key);
@@ -238,106 +237,17 @@ function planTransaction(registry, activeDir, shortIdLength) {
238
237
  taskIdToKey.set(taskId, key);
239
238
  }
240
239
 
241
- // A4: classify each active task
240
+ // The registry is the sole source of truth: short ids are allocated only by
241
+ // explicit `alloc` (planAlloc), never inferred from task.md or auto-allocated
242
+ // for active tasks. Read paths (resolve/list) only run stale cleanup (A2).
242
243
  const plannedRegistryWrites = [];
243
- const plannedTaskMdWrites = [];
244
- const pendingAlloc = [];
245
-
246
- for (const taskId of activeTaskIds) {
247
- const taskMdPath = path.join(activeDir, taskId, "task.md");
248
- const originalStat = fs.statSync(taskMdPath);
249
- const originalContent = fs.readFileSync(taskMdPath, "utf8");
250
- const existing = originalContent.match(/^short_id:\s*(#\d+)\s*$/m);
251
-
252
- if (existing) {
253
- const declared = existing[1];
254
- const n = declared.slice(1);
255
- if (projectedIds[n] === taskId) continue; // 4a
256
- if (taskIdToKey.has(taskId)) {
257
- const registryKey = taskIdToKey.get(taskId);
258
- writeStderr(
259
- `Inconsistent: task ${taskId} declares ${declared} but registry holds it at #${registryKey}\n`
260
- );
261
- process.exit(2);
262
- }
263
- if (projectedIds[n] && projectedIds[n] !== taskId) {
264
- writeStderr(
265
- `Inconsistent: task ${taskId} declares ${declared} but registry maps ${declared} to ${projectedIds[n]}\n`
266
- );
267
- process.exit(2);
268
- }
269
- // 4d
270
- plannedRegistryWrites.push({ key: n, taskId });
271
- projectedIds[n] = taskId;
272
- taskIdToKey.set(taskId, n);
273
- continue;
274
- }
275
-
276
- if (taskIdToKey.has(taskId)) {
277
- // 4e
278
- const registryKey = taskIdToKey.get(taskId);
279
- plannedTaskMdWrites.push({
280
- taskMdPath,
281
- originalContent,
282
- originalAtime: originalStat.atime,
283
- originalMtime: originalStat.mtime,
284
- shortId: `#${registryKey}`,
285
- kind: "4e"
286
- });
287
- continue;
288
- }
289
-
290
- // 4f: deferred
291
- pendingAlloc.push({
292
- taskId,
293
- taskMdPath,
294
- originalContent,
295
- originalAtime: originalStat.atime,
296
- originalMtime: originalStat.mtime
297
- });
298
- }
299
-
300
- // A5: capacity pre-check
301
- const availableSlots = maxN - Object.keys(projectedIds).length;
302
- if (pendingAlloc.length > availableSlots) {
303
- writeStderr(
304
- `Error: cold-start migration needs ${pendingAlloc.length} short id(s) but only ${availableSlots} ` +
305
- `slot(s) available (capacity=${maxN}, in-use after stale-cleanup=${Object.keys(projectedIds).length}). ` +
306
- `Archive some active tasks (complete-task / cancel-task / block-task) ` +
307
- `or raise task.shortIdLength in .agents/.airc.json.\n`
308
- );
309
- process.exit(2);
310
- }
311
-
312
- pendingAlloc.sort((a, b) => a.taskId.localeCompare(b.taskId));
313
-
314
- for (const item of pendingAlloc) {
315
- const n = allocateMinFreeInt({ ids: projectedIds }, shortIdLength);
316
- if (n === null) {
317
- throw new Error("Internal invariant: pendingAlloc capacity check failed");
318
- }
319
- const key = padShortId(n, shortIdLength);
320
- projectedIds[key] = item.taskId;
321
- taskIdToKey.set(item.taskId, key);
322
- plannedRegistryWrites.push({ key, taskId: item.taskId });
323
- plannedTaskMdWrites.push({
324
- taskMdPath: item.taskMdPath,
325
- originalContent: item.originalContent,
326
- originalAtime: item.originalAtime,
327
- originalMtime: item.originalMtime,
328
- shortId: `#${key}`,
329
- kind: "4f"
330
- });
331
- }
332
244
 
333
- // Build transaction object
334
245
  const tx = {
335
246
  _registry: registry,
336
247
  _activeDir: activeDir,
337
248
  _registrySnapshot: { ...registry.ids },
338
249
  _pendingRegistryDeletes: pendingRegistryDeletes,
339
250
  _plannedRegistryWrites: plannedRegistryWrites,
340
- _plannedTaskMdWrites: plannedTaskMdWrites,
341
251
  _projectedIds: projectedIds,
342
252
  _taskIdToKey: taskIdToKey,
343
253
  _shortIdLength: shortIdLength,
@@ -363,20 +273,6 @@ function planTransaction(registry, activeDir, shortIdLength) {
363
273
  this._projectedIds[key] = taskId;
364
274
  this._taskIdToKey.set(taskId, key);
365
275
  this._plannedRegistryWrites.push({ key, taskId });
366
- const originalStat = fs.statSync(taskMdPath);
367
- const originalContent = fs.readFileSync(taskMdPath, "utf8");
368
- // If task.md already declares the same short id (e.g. R-alloc replay), skip writing.
369
- const existing = originalContent.match(/^short_id:\s*(#\d+)\s*$/m);
370
- if (!existing || existing[1] !== `#${key}`) {
371
- this._plannedTaskMdWrites.push({
372
- taskMdPath,
373
- originalContent,
374
- originalAtime: originalStat.atime,
375
- originalMtime: originalStat.mtime,
376
- shortId: `#${key}`,
377
- kind: "caller-alloc"
378
- });
379
- }
380
276
  return key; // zero-padded; matches registry key
381
277
  },
382
278
 
@@ -386,54 +282,22 @@ function planTransaction(registry, activeDir, shortIdLength) {
386
282
  this._plannedRegistryWrites = this._plannedRegistryWrites.filter(
387
283
  (w) => w.taskId !== taskId
388
284
  );
389
- this._plannedTaskMdWrites = this._plannedTaskMdWrites.filter(
390
- (w) => path.basename(path.dirname(w.taskMdPath)) !== taskId
391
- );
392
285
  this._pendingRegistryDeletes.push(key);
393
286
  delete this._projectedIds[key];
394
287
  this._taskIdToKey.delete(taskId);
395
288
  },
396
289
 
397
290
  commit(registryPath) {
398
- // B1: apply registry mutation in memory
291
+ // Apply registry mutation in memory, then persist atomically.
399
292
  for (const key of this._pendingRegistryDeletes) delete this._registry.ids[key];
400
293
  for (const { key, taskId } of this._plannedRegistryWrites) {
401
294
  this._registry.ids[key] = taskId;
402
295
  }
403
-
404
- const completedWrites = [];
405
- const rollback = (reason) => {
406
- for (const done of completedWrites.reverse()) {
407
- try {
408
- fs.writeFileSync(done.taskMdPath, done.originalContent);
409
- fs.utimesSync(done.taskMdPath, done.originalAtime, done.originalMtime);
410
- } catch {
411
- /* best-effort */
412
- }
413
- }
414
- this._registry.ids = this._registrySnapshot;
415
- const tail =
416
- completedWrites.length > 0
417
- ? `; rolled back ${completedWrites.length} prior task.md write(s)`
418
- : "";
419
- throw new Error(`${reason}${tail}`);
420
- };
421
-
422
- // B2: write task.md per plan
423
- for (const write of this._plannedTaskMdWrites) {
424
- try {
425
- writeTaskMdShortId(write.taskMdPath, write.shortId);
426
- completedWrites.push(write);
427
- } catch (e) {
428
- rollback(`Failed to write short_id to ${write.taskMdPath}: ${e.message}`);
429
- }
430
- }
431
-
432
- // B3: atomic registry persistence
433
296
  try {
434
297
  writeRegistryAtomic(this._registry, registryPath);
435
298
  } catch (e) {
436
- rollback(`Failed to persist registry to ${registryPath}: ${e.message}`);
299
+ this._registry.ids = this._registrySnapshot;
300
+ throw new Error(`Failed to persist registry to ${registryPath}: ${e.message}`);
437
301
  }
438
302
  }
439
303
  };
@@ -449,27 +313,10 @@ function verifyRegistry(registry, activeDir) {
449
313
  .filter((d) => fs.existsSync(path.join(activeDir, d, "task.md")))
450
314
  );
451
315
  const registryTaskIds = new Set(Object.values(registry.ids));
452
- const taskmdShortIds = new Map();
453
- for (const taskId of activeTaskIds) {
454
- const taskMdPath = path.join(activeDir, taskId, "task.md");
455
- const content = fs.readFileSync(taskMdPath, "utf8");
456
- const m = content.match(/^short_id:\s*(#\d+)\s*$/m);
457
- taskmdShortIds.set(taskId, m ? m[1] : null);
458
- }
459
316
  const missing_in_registry = [];
460
317
  for (const taskId of activeTaskIds) {
461
318
  if (!registryTaskIds.has(taskId)) {
462
- missing_in_registry.push({ taskId, declared: taskmdShortIds.get(taskId) });
463
- }
464
- }
465
- const missing_in_taskmd = [];
466
- for (const [key, taskId] of Object.entries(registry.ids)) {
467
- if (!activeTaskIds.has(taskId)) continue;
468
- const declared = taskmdShortIds.get(taskId);
469
- if (declared === null) {
470
- missing_in_taskmd.push({ taskId, expected: `#${key}` });
471
- } else if (declared !== `#${key}`) {
472
- missing_in_taskmd.push({ taskId, expected: `#${key}`, declared });
319
+ missing_in_registry.push({ taskId });
473
320
  }
474
321
  }
475
322
  const orphans_in_registry = [];
@@ -491,7 +338,6 @@ function verifyRegistry(registry, activeDir) {
491
338
  }
492
339
  return {
493
340
  missing_in_registry,
494
- missing_in_taskmd,
495
341
  orphans_in_registry,
496
342
  duplicate_registry_keys
497
343
  };
@@ -548,8 +394,8 @@ function cmdRelease(taskId, activeDir, registryPath, shortIdLength) {
548
394
  }
549
395
 
550
396
  function cmdResolve(shortIdArg, activeDir, registryPath, shortIdLength) {
551
- // Strict width match + reserved key check; on invalid arg, parseShortIdArg writes full
552
- // stderr (including "expected #NN (N-digit zero-padded; e.g. '#01')") and exits 1.
397
+ // Accepts bare digits ('11') or '#'-prefixed form ('#11', '#11', '#005'); normalized by
398
+ // numeric value with capacity check (n > 10^L-1) and reserved-zero rejection.
553
399
  const key = parseShortIdArg(shortIdArg, shortIdLength);
554
400
  return withRegistryLock(activeDir, () => {
555
401
  const registry = readRegistry(registryPath);
@@ -558,8 +404,7 @@ function cmdResolve(shortIdArg, activeDir, registryPath, shortIdLength) {
558
404
  if (!taskId) {
559
405
  const hasPendingMutations =
560
406
  tx._plannedRegistryWrites.length > 0 ||
561
- tx._pendingRegistryDeletes.length > 0 ||
562
- tx._plannedTaskMdWrites.length > 0;
407
+ tx._pendingRegistryDeletes.length > 0;
563
408
  if (hasPendingMutations) {
564
409
  try {
565
410
  tx.commit(registryPath);
@@ -604,7 +449,6 @@ function cmdList(activeDir, registryPath, verify) {
604
449
  const diff = verifyRegistry(registry, activeDir);
605
450
  const hasIssues =
606
451
  diff.missing_in_registry.length > 0 ||
607
- diff.missing_in_taskmd.length > 0 ||
608
452
  diff.orphans_in_registry.length > 0 ||
609
453
  diff.duplicate_registry_keys.length > 0;
610
454
  if (hasIssues) {
@@ -699,7 +543,6 @@ export {
699
543
  readRegistry,
700
544
  writeRegistryAtomic,
701
545
  withRegistryLock,
702
- writeTaskMdShortId,
703
546
  padShortId,
704
547
  parseShortIdArg,
705
548
  allocateMinFreeInt,
@@ -29,7 +29,7 @@ Before the state check is complete, do not make external-state assertions such a
29
29
 
30
30
  ## Task id short ref
31
31
 
32
- > If `{task-id}` begins with `#`, follow the "SKILL parameter resolver" section of `.agents/rules/task-short-id.md`; treat `{task-id}` as the resolved full `TASK-YYYYMMDD-HHMMSS` form for every downstream command.
32
+ > If `{task-id}` matches `^[#]?[0-9]+$` (bare numeric or `#`-prefixed), follow the "SKILL parameter resolver" section of `.agents/rules/task-short-id.md`; treat `{task-id}` as the resolved full `TASK-YYYYMMDD-HHMMSS` form for every downstream command.
33
33
 
34
34
  ## Steps
35
35
 
@@ -65,6 +65,8 @@ If `task.md` contains these source fields, also read the corresponding source in
65
65
  - `codescan_alert_number` - Code Scanning alert
66
66
  - `security_alert_number` - Dependabot alert
67
67
 
68
+ **Round ≥ 2: respond to the prior review (only when a review artifact exists)**: if the task directory contains `review-analysis.md` / `review-analysis-r{N}.md`, read the highest-round review report; add a `## Response to Prior Review` section to this round's analysis artifact, and for each finding verify it via Read/Grep before acting (holds → accept and fix; judged hallucinated/unfounded → rebut with counter-evidence rather than defaulting to compliance); record any open disagreement under `## Open Questions`. Round 1 has no review, so skip this section.
69
+
68
70
  ### 4. Perform Requirements Analysis
69
71
 
70
72
  Before analysis begins: if `start_date` in the frontmatter is empty, write today's date immediately (command: `date +%F`, format `YYYY-MM-DD`); keep any existing value. Before writing, read `.agents/rules/version-stamp.md` and refresh `updated_at` / `agent_infra_version` at the same time.
@@ -154,15 +156,11 @@ Update `.agents/workspace/active/{task-id}/task.md`:
154
156
  - Mark requirement-analysis as complete in workflow progress and include the actual round when the task template supports it
155
157
  - Before appending the workflow Activity Log entry, re-estimate `priority` based on the analysis findings (business impact, risks, dependencies, blockers). If the re-estimated value differs from the current value in `task.md`:
156
158
  - Overwrite the `priority` field in frontmatter with the new value
157
- - Prepend an Activity Log entry recording the transition (placed before the `Requirement Analysis (Round N)` entry):
158
- ```
159
- - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Analysis Re-estimate** by {agent} — priority {old} → {new} (rationale: {short basis grounded in this analysis})
160
- ```
161
- Both entries may share the same timestamp; ordering is conveyed by list position only.
162
- If the re-estimated value matches the current value, skip the Re-estimate entry. The Flow A sync that follows reads the possibly updated frontmatter and propagates the new value to the Issue automatically.
159
+ - Append a `## Priority Re-estimate` section to this round's analysis artifact `{analysis-artifact}`, recording: `priority {old} → {new} (rationale: {short basis grounded in this analysis})`
160
+ If the re-estimated value matches the current value, skip it: do not write the `## Priority Re-estimate` section. The Flow A sync that follows reads the possibly updated frontmatter and propagates the new value to the Issue automatically.
163
161
  - **Append** to `## Activity Log` (do NOT overwrite previous entries):
164
162
  ```
165
- - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Requirement Analysis (Round {N})** by {agent} — Analysis completed → {analysis-artifact}
163
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Analyze Task (Round {N})** by {agent} — Analysis completed → {analysis-artifact}
166
164
  ```
167
165
 
168
166
  If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure):
@@ -191,7 +189,7 @@ Keep the gate output in your reply as fresh evidence. Do not claim completion wi
191
189
 
192
190
  > Execute this step only after the verification gate passes.
193
191
 
194
- > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name).
192
+ > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name). Before rendering the "Next steps" commands, read `.agents/rules/next-step-output.md` and use its short-id snippet to render `{task-ref}` in the commands as the short id `#NN` (falling back to the full TASK-id when unallocated or released).
195
193
 
196
194
  Output format:
197
195
  ```
@@ -206,9 +204,9 @@ Output file:
206
204
  - Analysis report: .agents/workspace/active/{task-id}/{analysis-artifact}
207
205
 
208
206
  Next step - review the analysis:
209
- - Claude Code / OpenCode: /review-analysis {task-id}
210
- - Gemini CLI: /{{project}}:review-analysis {task-id}
211
- - Codex CLI: $review-analysis {task-id}
207
+ - Claude Code / OpenCode: /review-analysis {task-ref}
208
+ - Gemini CLI: /{{project}}:review-analysis {task-ref}
209
+ - Codex CLI: $review-analysis {task-ref}
212
210
  ```
213
211
 
214
212
  ## Completion Checklist
@@ -29,7 +29,7 @@ tail .agents/workspace/active/{task-id}/task.md
29
29
 
30
30
  ## 任务入参短号别名
31
31
 
32
- > 如果 `{task-id}` 入参以 `#` 开头,先读取 `.agents/rules/task-short-id.md` 的「SKILL 入参解析」段执行解析;后续命令视 `{task-id}` 为解析后的全长 `TASK-YYYYMMDD-HHMMSS` 形式。
32
+ > 如果 `{task-id}` 入参匹配 `^[#]?[0-9]+$`(裸数字或带 `#` 前缀),先读取 `.agents/rules/task-short-id.md` 的「SKILL 入参解析」段执行解析;后续命令视 `{task-id}` 为解析后的全长 `TASK-YYYYMMDD-HHMMSS` 形式。
33
33
 
34
34
  ## 执行步骤
35
35
  ### 1. 验证前置条件
@@ -64,6 +64,8 @@ tail .agents/workspace/active/{task-id}/task.md
64
64
  - `codescan_alert_number` - Code Scanning 告警
65
65
  - `security_alert_number` - Dependabot 告警
66
66
 
67
+ **Round ≥ 2:响应上一轮审查(仅当存在审查产物时)**:若任务目录存在 `review-analysis.md` / `review-analysis-r{N}.md`,读取最高轮次的审查报告;在本轮分析产物中新增 `## 对上一轮审查的响应` 段,对每条发现先 Read/Grep 核实再处置(成立 → 接受并修正;判定为幻觉/不成立 → 附反证反驳,不默认顺从),未决分歧写入 `## 未决问题`。Round 1 无审查,跳过本段。
68
+
67
69
  ### 4. 执行需求分析
68
70
 
69
71
  开始分析前:若 frontmatter 的 `start_date` 为空,立即写入当日日期(命令 `date +%F`,格式 `YYYY-MM-DD`);已有值则保留。写入前先读取 `.agents/rules/version-stamp.md`,并同步刷新 `updated_at` / `agent_infra_version`。
@@ -153,15 +155,11 @@ date "+%Y-%m-%d %H:%M:%S%:z"
153
155
  - 在工作流进度中标记 requirement-analysis 为已完成,并注明实际轮次(如果任务模板支持)
154
156
  - 在追加工作流 Activity Log 条目之前,基于分析结果(业务影响、风险、依赖、阻塞条件)重估 `priority`。若重估值与 `task.md` 当前值不一致:
155
157
  - 用新值覆盖 frontmatter 的 `priority` 字段
156
- - `Requirement Analysis (Round N)` 条目之前追加一条转移记录:
157
- ```
158
- - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Analysis Re-estimate** by {agent} — priority {old} → {new} (rationale: {基于本轮分析的简短依据})
159
- ```
160
- 两条条目可共用同一时间戳,顺序仅通过列表位置表达。
161
- 若重估值与当前值一致,跳过 Re-estimate 条目。后续 Flow A 同步会读取可能更新过的 frontmatter,并自动把新值同步到 Issue。
158
+ - 在本轮分析产物 `{analysis-artifact}` 中追加 `## 优先级重估` 段,记录一条:`priority {old} → {new} (rationale: {基于本轮分析的简短依据})`
159
+ 若重估值与当前值一致,跳过:不写入 `## 优先级重估` 段。后续 Flow A 同步会读取可能更新过的 frontmatter,并自动把新值同步到 Issue。
162
160
  - **追加**到 `## Activity Log`(不要覆盖之前的记录):
163
161
  ```
164
- - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Requirement Analysis (Round {N})** by {agent} — Analysis completed → {analysis-artifact}
162
+ - {YYYY-MM-DD HH:mm:ss±HH:MM} — **Analyze Task (Round {N})** by {agent} — Analysis completed → {analysis-artifact}
165
163
  ```
166
164
 
167
165
  如果 task.md 中存在有效的 `issue_number`,执行以下同步操作(任一失败则跳过并继续):
@@ -190,7 +188,7 @@ node .agents/scripts/validate-artifact.js gate analyze-task .agents/workspace/ac
190
188
 
191
189
  > 仅在校验通过后执行本步骤。
192
190
 
193
- > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。如果 `.agents/.airc.json` 中配置了自定义 TUI(`customTUIs`),读取每个工具的 `name` 和 `invoke`,按同样格式补充对应命令行(`${skillName}` 替换为技能名,`${projectName}` 替换为项目名)。
191
+ > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。如果 `.agents/.airc.json` 中配置了自定义 TUI(`customTUIs`),读取每个工具的 `name` 和 `invoke`,按同样格式补充对应命令行(`${skillName}` 替换为技能名,`${projectName}` 替换为项目名)。 渲染「下一步」命令前,先读取 `.agents/rules/next-step-output.md`,按其取短号片段把命令中的 `{task-ref}` 渲染为短号 `#NN`(未分配/已释放时回退完整 TASK-id)。
194
192
 
195
193
  输出格式:
196
194
  ```
@@ -205,9 +203,9 @@ node .agents/scripts/validate-artifact.js gate analyze-task .agents/workspace/ac
205
203
  - 分析报告:.agents/workspace/active/{task-id}/{analysis-artifact}
206
204
 
207
205
  下一步 - 审查需求分析:
208
- - Claude Code / OpenCode:/review-analysis {task-id}
209
- - Gemini CLI:/agent-infra:review-analysis {task-id}
210
- - Codex CLI:$review-analysis {task-id}
206
+ - Claude Code / OpenCode:/review-analysis {task-ref}
207
+ - Gemini CLI:/agent-infra:review-analysis {task-ref}
208
+ - Codex CLI:$review-analysis {task-ref}
211
209
  ```
212
210
 
213
211
  ## 完成检查清单
@@ -32,7 +32,7 @@
32
32
  ]
33
33
  },
34
34
  "activity-log": {
35
- "expected_action_pattern": "Requirement Analysis \\(Round \\d+\\)",
35
+ "expected_action_pattern": "(Analyze Task|Requirement Analysis) \\(Round \\d+\\)",
36
36
  "freshness_minutes": 30
37
37
  },
38
38
  "platform-sync": {