@fitlab-ai/agent-infra 0.5.3 → 0.5.5

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 (69) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/lib/defaults.json +1 -0
  4. package/package.json +1 -1
  5. package/templates/.agents/rules/issue-pr-commands.github.en.md +35 -10
  6. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +35 -10
  7. package/templates/.agents/rules/issue-sync.github.en.md +118 -28
  8. package/templates/.agents/rules/issue-sync.github.zh-CN.md +112 -22
  9. package/templates/.agents/rules/milestone-inference.github.en.md +13 -6
  10. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +13 -6
  11. package/templates/.agents/rules/pr-sync.github.en.md +3 -1
  12. package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
  13. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +1118 -0
  14. package/templates/.agents/scripts/validate-artifact.js +51 -802
  15. package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
  16. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
  17. package/templates/.agents/skills/analyze-task/config/verify.json +1 -1
  18. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  19. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  20. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  21. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
  22. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
  23. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  24. package/templates/.agents/skills/commit/SKILL.en.md +14 -2
  25. package/templates/.agents/skills/commit/SKILL.zh-CN.md +14 -2
  26. package/templates/.agents/skills/commit/config/verify.json +2 -1
  27. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +23 -0
  28. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +23 -0
  29. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  30. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  31. package/templates/.agents/skills/complete-task/config/verify.json +1 -1
  32. package/templates/.agents/skills/create-issue/SKILL.en.md +3 -1
  33. package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +3 -1
  34. package/templates/.agents/skills/create-issue/config/verify.json +1 -1
  35. package/templates/.agents/skills/create-issue/reference/label-and-type.en.md +6 -1
  36. package/templates/.agents/skills/create-issue/reference/label-and-type.zh-CN.md +6 -1
  37. package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
  38. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
  39. package/templates/.agents/skills/create-pr/config/verify.json +3 -1
  40. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +9 -6
  41. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +9 -6
  42. package/templates/.agents/skills/create-release-note/SKILL.en.md +27 -2
  43. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +27 -2
  44. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  45. package/templates/.agents/skills/implement-task/SKILL.en.md +4 -4
  46. package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +4 -4
  47. package/templates/.agents/skills/implement-task/config/verify.json +1 -2
  48. package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
  49. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
  50. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  51. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  52. package/templates/.agents/skills/import-issue/config/verify.json +1 -1
  53. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  54. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  55. package/templates/.agents/skills/plan-task/config/verify.json +1 -1
  56. package/templates/.agents/skills/refine-task/SKILL.en.md +2 -4
  57. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +2 -4
  58. package/templates/.agents/skills/refine-task/config/verify.json +1 -2
  59. package/templates/.agents/skills/refine-title/SKILL.en.md +5 -1
  60. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +5 -1
  61. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  62. package/templates/.agents/skills/review-task/SKILL.en.md +2 -2
  63. package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
  64. package/templates/.agents/skills/review-task/config/verify.json +1 -1
  65. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +2 -1
  66. package/templates/.github/scripts/sync-labels-to-set.sh +110 -0
  67. package/templates/.github/workflows/metadata-sync.yml +118 -0
  68. package/templates/.github/workflows/pr-label.yml +66 -0
  69. package/templates/.github/workflows/status-label.yml +20 -30
package/README.md CHANGED
@@ -410,7 +410,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
410
410
  "project": "my-project",
411
411
  "org": "my-org",
412
412
  "language": "en",
413
- "templateVersion": "v0.5.3",
413
+ "templateVersion": "v0.5.5",
414
414
  "files": {
415
415
  "managed": [
416
416
  ".agents/workspace/README.md",
package/README.zh-CN.md CHANGED
@@ -410,7 +410,7 @@ import-issue #42 从 GitHub Issue 导入任务
410
410
  "project": "my-project",
411
411
  "org": "my-org",
412
412
  "language": "en",
413
- "templateVersion": "v0.5.3",
413
+ "templateVersion": "v0.5.5",
414
414
  "files": {
415
415
  "managed": [
416
416
  ".agents/workspace/README.md",
package/lib/defaults.json CHANGED
@@ -36,6 +36,7 @@
36
36
  ".claude/hooks/",
37
37
  ".gemini/commands/",
38
38
  ".github/hooks/check-version-format.sh",
39
+ ".github/scripts/",
39
40
  ".opencode/commands/"
40
41
  ],
41
42
  "merged": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,50 +13,65 @@ gh repo view --json nameWithOwner
13
13
 
14
14
  If either command fails, stop or degrade according to the calling skill.
15
15
 
16
+ ## Upstream Repository and Permission Detection
17
+
18
+ Before any later `gh issue` or `gh api "repos/..."` call, follow `.agents/rules/issue-sync.md` to resolve `upstream_repo`, `has_triage`, and `has_push`.
19
+
20
+ - every later `gh issue` command must use `-R "$upstream_repo"`
21
+ - every later repository-scoped `gh api` command must use `"repos/$upstream_repo/..."`
22
+ - keep `gh pr *` commands on the current repository without adding `-R`
23
+ - keep organization-scoped commands such as `gh api "orgs/{owner}/..."` unchanged
24
+
16
25
  ## Read and Create Issues
17
26
 
18
27
  Read an Issue:
19
28
 
20
29
  ```bash
21
- gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
30
+ gh issue view {issue-number} -R "$upstream_repo" --json number,title,body,labels,state,milestone,url
22
31
  ```
23
32
 
24
33
  Create an Issue:
25
34
 
26
35
  ```bash
27
- gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
36
+ gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
28
37
  ```
29
38
 
30
39
  - expand `{label-args}` into repeated `--label` flags from the validated label list
40
+ - pass `{label-args}` only when `has_triage=true`; otherwise omit it and continue
31
41
  - omit all `--label` flags when nothing valid remains
42
+ - pass `{milestone-arg}` only when `has_triage=true`; otherwise omit it and continue
32
43
  - omit `{milestone-arg}` entirely when no milestone should be set
33
44
 
34
45
  Set the Issue Type:
35
46
 
36
47
  ```bash
37
48
  gh api "orgs/{owner}/issue-types" --jq '.[].name'
38
- gh api "repos/{owner}/{repo}/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
49
+ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
39
50
  ```
40
51
 
52
+ - set the Issue Type only when `has_push=true`; otherwise skip and continue
53
+
41
54
  ## Update Issues
42
55
 
43
56
  Use this shape when updating titles, labels, assignees, or milestones:
44
57
 
45
58
  ```bash
46
- gh issue edit {issue-number} {edit-args}
59
+ gh issue edit {issue-number} -R "$upstream_repo" {edit-args}
47
60
  ```
48
61
 
49
62
  Common arguments:
50
63
  - `--title "{title}"`
51
- - `--add-label "{label}"`
52
- - `--remove-label "{label}"`
64
+ - `--add-label "{label}"` (only when `has_triage=true`)
65
+ - `--remove-label "{label}"` (only when `has_triage=true`)
53
66
  - `--add-assignee @me`
54
- - `--milestone "{milestone}"`
67
+ - `--milestone "{milestone}"` (only when `has_triage=true`)
68
+
69
+ Do not pre-check assignee permissions. If the assignee command fails, silently skip it per the caller's contract.
55
70
 
56
71
  Close an Issue:
57
72
 
58
73
  ```bash
59
- gh issue close {issue-number} --reason "{reason}"
74
+ gh issue close {issue-number} -R "$upstream_repo" --reason "{reason}"
60
75
  ```
61
76
 
62
77
  ## Read Issue Comments
@@ -64,7 +79,7 @@ gh issue close {issue-number} --reason "{reason}"
64
79
  Read Issue comments or search for existing hidden markers:
65
80
 
66
81
  ```bash
67
- gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" --paginate
82
+ gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
68
83
  ```
69
84
 
70
85
  ## Read and Create PRs
@@ -84,12 +99,21 @@ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefN
84
99
  Create a PR:
85
100
 
86
101
  ```bash
87
- gh pr create --base "{target-branch}" --title "{title}" --assignee @me --body "$(cat <<'EOF'
102
+ gh pr create --base "{target-branch}" --title "{title}" --assignee @me \
103
+ {label-args} {milestone-arg} \
104
+ --body "$(cat <<'EOF'
88
105
  {pr-body}
89
106
  EOF
90
107
  )"
91
108
  ```
92
109
 
110
+ - expand `{label-args}` into repeated `--label "{label}"` flags from the validated label list
111
+ - pass `{label-args}` only when `has_triage=true`; otherwise omit it and continue
112
+ - omit all `--label` flags when nothing valid remains
113
+ - expand `{milestone-arg}` into `--milestone "{milestone}"`
114
+ - pass `{milestone-arg}` only when `has_triage=true`; otherwise omit it and continue
115
+ - omit `{milestone-arg}` entirely when no milestone should be set
116
+
93
117
  ## Update PRs
94
118
 
95
119
  Update PR titles, labels, or milestones with:
@@ -108,4 +132,5 @@ Common arguments:
108
132
 
109
133
  - read failures: stop or skip based on the calling skill
110
134
  - update failures: warn and continue when the caller marks the action as best-effort
135
+ - insufficient permission: skip direct writes according to the `has_triage` / `has_push` branch and continue
111
136
  - `@me` is resolved by `gh` CLI to the authenticated user
@@ -13,50 +13,65 @@ gh repo view --json nameWithOwner
13
13
 
14
14
  如果任一命令失败,按调用该规则的 skill 约定停止或降级。
15
15
 
16
+ ## Upstream 仓库与权限检测
17
+
18
+ 在后续任何 `gh issue` 或 `gh api "repos/..."` 操作之前,先按 `.agents/rules/issue-sync.md` 完成 `upstream_repo`、`has_triage` 和 `has_push` 检测。
19
+
20
+ - 后续所有 `gh issue` 命令统一使用 `-R "$upstream_repo"`
21
+ - 后续所有 repo 级 `gh api` 命令统一使用 `"repos/$upstream_repo/..."`
22
+ - `gh pr *` 命令保持作用于当前仓库,不额外加 `-R`
23
+ - `gh api "orgs/{owner}/..."` 这类 org 级命令保持不变
24
+
16
25
  ## Issue 读取与创建
17
26
 
18
27
  读取 Issue:
19
28
 
20
29
  ```bash
21
- gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
30
+ gh issue view {issue-number} -R "$upstream_repo" --json number,title,body,labels,state,milestone,url
22
31
  ```
23
32
 
24
33
  创建 Issue:
25
34
 
26
35
  ```bash
27
- gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
36
+ gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
28
37
  ```
29
38
 
30
39
  - `{label-args}` 由调用方按有效 label 列表展开为多个 `--label`
40
+ - 仅当 `has_triage=true` 时传入 `{label-args}`;否则整体省略并继续
31
41
  - 没有有效 label 时省略全部 `--label`
42
+ - 仅当 `has_triage=true` 时传入 `{milestone-arg}`;否则整体省略并继续
32
43
  - `{milestone-arg}` 为空时整体省略
33
44
 
34
45
  设置 Issue Type:
35
46
 
36
47
  ```bash
37
48
  gh api "orgs/{owner}/issue-types" --jq '.[].name'
38
- gh api "repos/{owner}/{repo}/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
49
+ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
39
50
  ```
40
51
 
52
+ - 仅当 `has_push=true` 时执行 Issue Type 设置;否则跳过并继续
53
+
41
54
  ## Issue 更新
42
55
 
43
56
  更新标题、label、assignee 或 milestone 时使用:
44
57
 
45
58
  ```bash
46
- gh issue edit {issue-number} {edit-args}
59
+ gh issue edit {issue-number} -R "$upstream_repo" {edit-args}
47
60
  ```
48
61
 
49
62
  常见参数:
50
63
  - `--title "{title}"`
51
- - `--add-label "{label}"`
52
- - `--remove-label "{label}"`
64
+ - `--add-label "{label}"`(仅当 `has_triage=true`)
65
+ - `--remove-label "{label}"`(仅当 `has_triage=true`)
53
66
  - `--add-assignee @me`
54
- - `--milestone "{milestone}"`
67
+ - `--milestone "{milestone}"`(仅当 `has_triage=true`)
68
+
69
+ Assignee 同步不做权限预判;如果命令失败,按调用方约定静默跳过。
55
70
 
56
71
  关闭 Issue:
57
72
 
58
73
  ```bash
59
- gh issue close {issue-number} --reason "{reason}"
74
+ gh issue close {issue-number} -R "$upstream_repo" --reason "{reason}"
60
75
  ```
61
76
 
62
77
  ## Issue 评论读取
@@ -64,7 +79,7 @@ gh issue close {issue-number} --reason "{reason}"
64
79
  读取 Issue 评论或按隐藏标记查找已有评论:
65
80
 
66
81
  ```bash
67
- gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" --paginate
82
+ gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
68
83
  ```
69
84
 
70
85
  ## PR 读取与创建
@@ -84,12 +99,21 @@ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefN
84
99
  创建 PR:
85
100
 
86
101
  ```bash
87
- gh pr create --base "{target-branch}" --title "{title}" --assignee @me --body "$(cat <<'EOF'
102
+ gh pr create --base "{target-branch}" --title "{title}" --assignee @me \
103
+ {label-args} {milestone-arg} \
104
+ --body "$(cat <<'EOF'
88
105
  {pr-body}
89
106
  EOF
90
107
  )"
91
108
  ```
92
109
 
110
+ - `{label-args}` 由调用方按有效 label 列表展开为多个 `--label "{label}"`
111
+ - 仅当 `has_triage=true` 时传入 `{label-args}`;否则整体省略并继续
112
+ - 没有有效 label 时省略全部 `--label`
113
+ - `{milestone-arg}` 展开为 `--milestone "{milestone}"`
114
+ - 仅当 `has_triage=true` 时传入 `{milestone-arg}`;否则整体省略并继续
115
+ - `{milestone-arg}` 为空时整体省略
116
+
93
117
  ## PR 更新
94
118
 
95
119
  更新 PR 标题、label 或 milestone:
@@ -108,4 +132,5 @@ gh pr edit {pr-number} {edit-args}
108
132
 
109
133
  - 读取失败:按调用方规则决定停止还是跳过
110
134
  - 更新失败:如果调用方标记为 best-effort,输出警告并继续
135
+ - 权限不足:按 `has_triage` / `has_push` 分支跳过直接写操作,不阻塞调用方
111
136
  - `@me` 由 `gh` CLI 解析为当前认证用户
@@ -2,45 +2,121 @@
2
2
 
3
3
  Read this file before a task skill updates a GitHub Issue.
4
4
 
5
+ ## Upstream Repository Detection
6
+
7
+ When an external contributor runs `gh` inside a fork, the default target is the fork instead of the upstream repository. Detect the upstream repository first and reuse `upstream_repo` for every later `gh issue` and `gh api "repos/..."` operation.
8
+
9
+ ```bash
10
+ upstream_repo=$(gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
11
+ --jq 'if .fork then .parent.full_name else .full_name end' 2>/dev/null)
12
+ ```
13
+
14
+ - non-fork repository: returns the current repository `full_name`
15
+ - fork repository: returns the parent repository `full_name`
16
+ - every later `gh issue` command must use `-R "$upstream_repo"`
17
+ - every later `gh api "repos/..."` command must use `"repos/$upstream_repo/..."`
18
+
19
+ ## Permission Detection
20
+
21
+ Run one permission check against the upstream repository before any write operation. When detection fails, treat it as no permission so the workflow degrades safely.
22
+
23
+ ```bash
24
+ repo_perms=$(gh api "repos/$upstream_repo" --jq '.permissions' 2>/dev/null || echo '{}')
25
+ has_triage=$(printf '%s' "$repo_perms" | grep -q '"triage":true' 2>/dev/null && echo true || echo false)
26
+ has_push=$(printf '%s' "$repo_perms" | grep -q '"push":true' 2>/dev/null && echo true || echo false)
27
+ ```
28
+
29
+ Operation-to-permission mapping:
30
+
31
+ | Operation | Required permission | Notes |
32
+ |------|---------|------|
33
+ | add/remove labels | `has_triage` | triage is the minimum permission |
34
+ | add/remove milestones | `has_triage` | same as above |
35
+ | edit Issue body | `has_triage` | used by requirement checkbox sync |
36
+ | set Issue Type | `has_push` | requires write permission |
37
+ | set assignee | no check | skip directly when it fails |
38
+ | publish/update comments | no check | allowed for authenticated users in public repositories |
39
+
40
+ ## Degradation Rules
41
+
42
+ | Level | Operation type | With permission | Without permission |
43
+ |------|---------|--------|--------|
44
+ | silent degradation | label / milestone / Issue Type | run the `gh` command directly and also update the task comment | skip direct `gh` writes, update only the task comment, let the bot backfill |
45
+ | direct skip | assignee | run the `gh` command directly | do nothing else |
46
+ | normal execution | comments | run normally | run normally |
47
+
48
+ Key rules:
49
+
50
+ - task comment sync must continue whether write permission exists or not
51
+ - insufficient permission only affects direct Issue metadata writes and must not stop the skill
52
+ - keep the existing `2>/dev/null || true` error-tolerance pattern
53
+
54
+ ## External Contributor Locking
55
+
56
+ Maintainers (`has_triage=true`) are never blocked. External contributors (`has_triage=false`) must check whether the current task already has a `task` comment author on the Issue before they start.
57
+
58
+ ```bash
59
+ task_comment_author=$(gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
60
+ --paginate --jq '[.[] | select(.body | test("<!-- sync-issue:{task-id}:task -->")) | .user.login] | first' \
61
+ 2>/dev/null || echo "")
62
+ current_user=$(gh api user --jq '.login' 2>/dev/null || echo "")
63
+ ```
64
+
65
+ Decision rules:
66
+
67
+ - no `task` comment exists: allow execution
68
+ - the `task` comment author is the current user: allow continuation
69
+ - the `task` comment author is another user: stop immediately and ask the contributor to coordinate with a maintainer before taking over
70
+
5
71
  ## Direct `status:` Label Updates
6
72
 
7
- If task.md contains a valid `issue_number` (not empty and not `N/A`) and the Issue state is `OPEN`, replace every existing `status:` label and add the target one:
73
+ Algorithm note: keep the flow below aligned with `.github/scripts/sync-labels-to-set.sh` (set-diff sync). This is the AI agent-side equivalent implementation for the `target_set = {"{target-status-label}"}` case. If either side changes, update the other one in the same patch to avoid drift between the agent and the bot.
74
+
75
+ If task.md contains a valid `issue_number` (not empty and not `N/A`) and the Issue state is `OPEN`, sync the `status:` labels to the target value with an idempotent set diff:
8
76
 
9
77
  ```bash
10
- state=$(gh issue view {issue-number} --json state --jq '.state' 2>/dev/null)
78
+ state=$(gh issue view {issue-number} -R "$upstream_repo" --json state --jq '.state' 2>/dev/null)
11
79
  if [ "$state" = "OPEN" ]; then
12
- gh issue view {issue-number} --json labels \
13
- --jq '.labels[].name | select(startswith("status:"))' 2>/dev/null \
14
- | while IFS= read -r label; do
15
- [ -z "$label" ] && continue
16
- gh issue edit {issue-number} --remove-label "$label" 2>/dev/null || true
17
- done
18
- gh issue edit {issue-number} --add-label "{target-status-label}" 2>/dev/null || true
80
+ current_status_labels=$(gh issue view {issue-number} -R "$upstream_repo" \
81
+ --json labels --jq '.labels[].name | select(startswith("status:"))' 2>/dev/null || true)
82
+ printf '%s\n' "$current_status_labels" | while IFS= read -r label; do
83
+ [ -z "$label" ] && continue
84
+ if [ "$label" != "{target-status-label}" ] && [ "$has_triage" = "true" ]; then
85
+ gh issue edit {issue-number} -R "$upstream_repo" --remove-label "$label" 2>/dev/null || true
86
+ fi
87
+ done
88
+ if [ "$has_triage" = "true" ] && ! printf '%s\n' "$current_status_labels" | grep -qxF "{target-status-label}"; then
89
+ gh issue edit {issue-number} -R "$upstream_repo" --add-label "{target-status-label}" 2>/dev/null || true
90
+ fi
19
91
  fi
20
92
  ```
21
93
 
22
94
  Use `while IFS= read -r label` so labels like `status: in-progress` are handled line-by-line instead of being split on spaces.
23
95
 
96
+ If `has_triage=false`, skip direct label changes, update only the task comment, and let the bot backfill from the latest task metadata.
97
+
24
98
  If `gh` fails, skip and continue. Do not fail the skill.
25
99
 
26
100
  ## Assignee Sync
27
101
 
28
102
  When a skill creates or imports an Issue, automatically add the current executor as assignee:
29
103
 
30
- - `create-issue`: use `--assignee @me` in the `gh issue create` command
31
- - `import-issue`: run `gh issue edit {issue-number} --add-assignee @me 2>/dev/null || true` after import
104
+ - `create-issue`: use `--assignee @me` in `gh issue create` and include `-R "$upstream_repo"`
105
+ - `import-issue`: run `gh issue edit {issue-number} -R "$upstream_repo" --add-assignee @me 2>/dev/null || true` after import
32
106
 
33
- `@me` is resolved by `gh` CLI to the authenticated user. The operation is idempotent (adding an existing assignee is a no-op). If the command fails (e.g. insufficient permissions), skip and continue.
107
+ `@me` is resolved by `gh` CLI to the authenticated user. The operation is idempotent. If the command fails, skip it directly and do not provide a fallback path.
34
108
 
35
109
  ## `in:` Label Sync
36
110
 
111
+ > **Trigger timing**: run `in:` label sync only after code is committed (the `commit` skill). Do not run it during `implement-task` or `refine-task`. During `create-pr`, only copy the labels from the Issue to the PR without recomputing them.
112
+
37
113
  Read the `labels.in` mapping from `.agents/.airc.json`.
38
114
 
39
115
  ```bash
40
116
  git diff {base-branch}...HEAD --name-only
41
117
  ```
42
118
 
43
- `{base-branch}` is usually `main`; in PR context, use the PR's base branch.
119
+ `{base-branch}` is usually `main`; in PR context, use the PR base branch.
44
120
 
45
121
  ### When a mapping exists (precise add/remove)
46
122
 
@@ -48,15 +124,18 @@ git diff {base-branch}...HEAD --name-only
48
124
  2. Match each file against the directory prefixes in `labels.in` to compute the expected `in:` label set
49
125
  3. Query the current `in:` labels on the Issue or PR
50
126
  4. Apply the diff:
51
- - expected but missing -> `gh issue edit {issue-number} --add-label "in: {module}" 2>/dev/null || true`
52
- - present but no longer expected -> `gh issue edit {issue-number} --remove-label "in: {module}" 2>/dev/null || true`
127
+ - expected but missing: only when `has_triage=true`, run `gh issue edit {issue-number} -R "$upstream_repo" --add-label "in: {module}" 2>/dev/null || true`
128
+ - present but no longer expected: only when `has_triage=true`, run `gh issue edit {issue-number} -R "$upstream_repo" --remove-label "in: {module}" 2>/dev/null || true`
53
129
 
54
130
  ### When no mapping exists (add-only fallback)
55
131
 
56
132
  If `.airc.json` has no `labels.in` field or it is empty:
133
+
57
134
  1. query existing repository `in:` labels
58
135
  2. derive the top-level directory from each changed file
59
- 3. add matching labels only and never remove existing `in:` labels
136
+ 3. only when `has_triage=true`, add matching labels and never remove existing `in:` labels
137
+
138
+ If `has_triage=false`, skip direct `in:` label edits and keep task comment sync as the source for later automation backfill.
60
139
 
61
140
  ## Artifact Comment Publishing
62
141
 
@@ -69,7 +148,7 @@ The hidden marker must remain compatible:
69
148
  Check for an existing comment before publishing:
70
149
 
71
150
  ```bash
72
- gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" \
151
+ gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
73
152
  --paginate --jq '.[].body' \
74
153
  | grep -qF "<!-- sync-issue:{task-id}:{file-stem} -->"
75
154
  ```
@@ -99,20 +178,23 @@ Use this format:
99
178
  `{agent}` is the name of the AI agent currently executing the skill (for example `claude`, `codex`, or `gemini`).
100
179
 
101
180
  `summary` comments need extra handling:
181
+
102
182
  - find an existing `<!-- sync-issue:{task-id}:summary -->` comment ID first
103
183
  - create the comment when none exists
104
- - patch the existing comment in place when the body changed
184
+ - patch the existing comment in place when the body changed by using `gh api "repos/$upstream_repo/issues/comments/{comment-id}" -X PATCH -f body=...`
105
185
 
106
186
  ```bash
107
- summary_comment_id=$(gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" \
187
+ summary_comment_id=$(gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
108
188
  --paginate --jq '.[] | select(.body | startswith("<!-- sync-issue:{task-id}:summary -->")) | .id' \
109
189
  | head -n 1)
110
- gh api "repos/{owner}/{repo}/issues/comments/{comment-id}" -X PATCH -f body="$(cat <<'EOF'
190
+ gh api "repos/$upstream_repo/issues/comments/{comment-id}" -X PATCH -f body="$(cat <<'EOF'
111
191
  {comment-body}
112
192
  EOF
113
193
  )"
114
194
  ```
115
195
 
196
+ Comment publishing is not gated by `has_triage` or `has_push`.
197
+
116
198
  ## task.md Comment Sync
117
199
 
118
200
  Hidden marker:
@@ -124,7 +206,7 @@ Hidden marker:
124
206
  Use an idempotent update path for `task.md`:
125
207
 
126
208
  1. Read the full `task.md`
127
- 2. Wrap the YAML frontmatter (content between the `---` delimiters) inside a `<details><summary>Metadata (frontmatter)</summary>` block with a `yaml` code fence; render the remaining body as normal Markdown
209
+ 2. Wrap the YAML frontmatter (content between the `---` delimiters) inside a `<details><summary>Metadata (frontmatter)</summary>` block with a `yaml` code fence, then render the remaining body as normal Markdown
128
210
  3. Use `task` as `{file-stem}`
129
211
  4. Find an existing comment ID for the marker
130
212
  5. Create the comment when none exists
@@ -155,20 +237,23 @@ task.md comment format:
155
237
  *Generated by {agent} · Internal tracking: {task-id}*
156
238
  ```
157
239
 
158
- When restoring, extract the frontmatter from the `<details>` block and reassemble with the body to recover the original `task.md`.
240
+ When restoring, extract the frontmatter from the `<details>` block and reassemble it with the body to recover the original `task.md`.
159
241
 
160
242
  Title mapping:
243
+
161
244
  - `task` -> `Task File`
162
245
 
246
+ task comment sync always runs and is never downgraded.
247
+
163
248
  ## Backfill Rules (run before `/complete-task` archives)
164
249
 
165
250
  - Scan `task.md`, `analysis*.md`, `plan*.md`, `implementation*.md`, `review*.md`, and `refinement*.md` in the task directory
166
251
  - Check whether each `{file-stem}` was already published by its hidden marker; publish only missing artifacts
167
- - Backfill only appends missing comments; never delete or reorder existing comments
252
+ - Backfill only appends missing comments and never deletes or reorders existing comments
168
253
  - Resolve `{agent}` for backfilled comments in this order:
169
- 1. Match the artifact filename in Activity Log (for example `→ analysis.md`) and extract the executor from `by {agent}`
170
- 2. If no match is found, fall back to `assigned_to` in task.md frontmatter
171
- 3. If `assigned_to` is also unavailable, use the current backfilling agent
254
+ 1. match the artifact filename in Activity Log (for example `→ analysis.md`) and extract the executor from `by {agent}`
255
+ 2. if no match is found, fall back to `assigned_to` in task.md frontmatter
256
+ 3. if `assigned_to` is also unavailable, use the current backfilling agent
172
257
  - Derive the previous and next neighbors from Activity Log order and add this note below the title:
173
258
 
174
259
  ```markdown
@@ -178,6 +263,7 @@ Title mapping:
178
263
  - If only one neighbor exists, keep only that side of the note; if neither exists, omit the note
179
264
 
180
265
  Title mapping:
266
+
181
267
  - `task` -> `Task File`
182
268
  - `analysis` / `analysis-r{N}` -> `Requirements Analysis` / `Requirements Analysis (Round {N})`
183
269
  - `plan` / `plan-r{N}` -> `Technical Plan` / `Technical Plan (Round {N})`
@@ -186,6 +272,8 @@ Title mapping:
186
272
  - `refinement` / `refinement-r{N}` -> `Refinement Report (Round 1)` / `Refinement Report (Round {N})`
187
273
  - `summary` -> `Delivery Summary`
188
274
 
275
+ Backfilled comments are also not gated by `has_triage` or `has_push`.
276
+
189
277
  ## Requirement Checkbox Sync
190
278
 
191
279
  Extract checked `- [x]` items from the `## Requirements` section in task.md. Skip when none exist.
@@ -193,10 +281,12 @@ Extract checked `- [x]` items from the `## Requirements` section in task.md. Ski
193
281
  Read the current Issue body:
194
282
 
195
283
  ```bash
196
- gh issue view {issue-number} --json body --jq '.body'
284
+ gh issue view {issue-number} -R "$upstream_repo" --json body --jq '.body'
197
285
  ```
198
286
 
199
- Replace matching `- [ ] {text}` lines with `- [x] {text}` only when the body actually changes, then PATCH the full body with `gh api`.
287
+ Replace matching `- [ ] {text}` lines with `- [x] {text}`. Use `gh api` to PATCH the full body only when the body changed and `has_triage=true`.
288
+
289
+ If `has_triage=false`, skip the body PATCH, update only the task comment, and let the bot backfill from the latest task state.
200
290
 
201
291
  ## Shell Safety Rules
202
292