Merge pull request 'fix: restore triage skill files and keep sync non-destructive' (#104) from develop into master

Reviewed-on: #104
This commit is contained in:
2026-05-15 03:34:26 +00:00
11 changed files with 140 additions and 21 deletions
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
When the task is to triage review findings, follow this workflow:
1. Merge all findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1 after sorting.
5. Fix real issues with the smallest safe change.
6. Add false positives to `.gitea/ai-review/exclusions.json`.
7. Add or update tests when behavior changes.
8. Re-check the issue after each fix.
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
+28
View File
@@ -0,0 +1,28 @@
---
name: triage-findings
description: Triage findings, fix real issues, and exclude false positives.
---
# Triage Findings
## Use
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
## Workflow
1. Merge all findings.
2. Sort by severity:
- critical
- warning
- info
3. Renumber from 1.
4. Fix real issues.
5. Put false positives into `.gitea/ai-review/exclusions.json`.
6. Add tests when behavior changes.
## Output Rules
- Keep the final list short.
- Keep numbering contiguous.
- Preserve file path, location, and fix.
+28
View File
@@ -0,0 +1,28 @@
---
name: triage-findings
description: Triage findings, fix real issues, and exclude false positives.
---
# Triage Findings
## Use
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
## Workflow
1. Merge all findings.
2. Sort by severity:
- critical
- warning
- info
3. Renumber from 1.
4. Fix real issues.
5. Put false positives into `.gitea/ai-review/exclusions.json`.
6. Add tests when behavior changes.
## Output Rules
- Keep the final list short.
- Keep numbering contiguous.
- Preserve file path, location, and fix.
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The full reusable skill lives in `.claude/skills/triage-findings/SKILL.md`.
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
+16
View File
@@ -0,0 +1,16 @@
# Triage Findings
When the task is to triage review findings, follow this workflow:
1. Merge all findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1 after sorting.
5. Fix real issues with the smallest safe change.
6. Add false positives to `.gitea/ai-review/exclusions.json`.
7. Add or update tests when behavior changes.
8. Re-check the issue after each fix.
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
Trigger it with `/triage-findings`.
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
+2 -2
View File
@@ -11,7 +11,7 @@
5. 從PR問題表格中取出所有舊問題,依照等級排序後 Comment 到 Push Request 5. 從PR問題表格中取出所有舊問題,依照等級排序後 Comment 到 Push Request
6. 從PR問題表格中取出所有新問題,排除嚴重等級的問題後 Comment 到 Push Request 6. 從PR問題表格中取出所有新問題,排除嚴重等級的問題後 Comment 到 Push Request
7. 從PR問題表格中取出所有新問題,將每個嚴重等級的問題 Comment 到 Push Request 7. 從PR問題表格中取出所有新問題,將每個嚴重等級的問題 Comment 到 Push Request
8. Commit 問題檔案,將 workspace 中實際存在的同步檔覆蓋到記憶區;workspace 不存在但記憶區已存在的同步檔則移除後再同步,避免 `git add` 因缺檔 pathspec 失敗 8. Commit 問題檔案,將 workspace 中實際存在的同步檔覆蓋到記憶區;workspace 沒有的同步檔就略過,不會刪除記憶區既有內容
9. 如果PR問題表格中有嚴重問題,則不要讓 workflow 執行成功(exit 1) 9. 如果PR問題表格中有嚴重問題,則不要讓 workflow 執行成功(exit 1)
# 設計 # 設計
@@ -227,4 +227,4 @@ Amazon Q:直接輸入 `triage-findings 問題原始檔(文字或截圖)`
### 版本包含 ### 版本包含
提交時一併包含 `triage-findings` skill 與各平台入口檔;已存在檔案一律覆蓋,同步到最新內容;若 workspace 已移除某個同步檔,記憶區中的對應檔案也會一併刪除,讓 commit 結果與 workspace 保持一致 提交時一併包含 `triage-findings` skill 與各平台入口檔;已存在檔案一律覆蓋,同步到最新內容;若 workspace 沒有某個同步檔,記憶區會保留原檔,不做刪除
+3 -3
View File
@@ -38,9 +38,9 @@
- 已驗收:log 已明確顯示 `.gitea/ai-review/findings.json``.gitea/ai-review/exclusions.json` 都是 `JSON 格式正確` - 已驗收:log 已明確顯示 `.gitea/ai-review/findings.json``.gitea/ai-review/exclusions.json` 都是 `JSON 格式正確`
## 階段八:記憶區 commit/push 與錯誤處理 ## 階段八:記憶區 commit/push 與錯誤處理
- 目標:記憶區能成功 commit/push,且一併包含 `triage-findings` skill 與各平台入口檔;skill 檔案已存在時一律以來源覆蓋,缺少的同步檔則記憶區刪除,達到鏡像同步效果;錯誤時有明確 log,流程結束有總結訊息。 - 目標:記憶區能成功 commit/push,且一併包含 `triage-findings` skill 與各平台入口檔;skill 檔案已存在時一律以來源覆蓋,workspace 沒有的同步檔則保留記憶區既有內容,不做刪除;錯誤時有明確 log,流程結束有總結訊息。
- 驗收:log 有「persisted findings」、「commit=...」、「push=...」等訊息,且能看出 skill 相關檔案已一併提交並被鏡像同步;當 workspace 不再包含某個同步檔時,記憶區中的對應檔案會被刪除且不再出現在 commit;錯誤時有「Runner failed: ...」等明確錯誤說明。 - 驗收:log 有「persisted findings」、「commit=...」、「push=...」等訊息,且能看出 skill 相關檔案已一併提交並被來源覆蓋;當 workspace 缺少某個同步檔時,記憶區中的對應檔案會被刪除;錯誤時有「Runner failed: ...」等明確錯誤說明。
- 已驗收:log 已出現 `persisted findings commit=79506eb push=整理程式碼`,代表 commit/push 成功;本次已補上缺檔刪除的鏡像同步邏輯,相關單元測試也已覆蓋。 - 已驗收:log 已出現 `persisted findings commit=79506eb push=整理程式碼`,代表 commit/push 成功;本次已補上「來源覆蓋、缺檔刪除」的同步規則,相關單元測試也已覆蓋。
## 階段九:阻擋嚴重問題 PR(第 8 點) ## 階段九:阻擋嚴重問題 PR(第 8 點)
- 目標:如果 PR 問題表格中有嚴重(critical)問題,workflow 需直接 exit 1,不讓流程成功。 - 目標:如果 PR 問題表格中有嚴重(critical)問題,workflow 需直接 exit 1,不讓流程成功。
+2 -11
View File
@@ -66,9 +66,9 @@ export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync)
run(['config', 'user.name', 'AI Review Bot'], repoDir); run(['config', 'user.name', 'AI Review Bot'], repoDir);
const existingSyncPaths = []; const existingSyncPaths = [];
const missingSyncPaths = [];
// Mirror sync files from workspace into the repo copy. // Copy action skill files into the target repo. Existing files are overwritten;
// missing source files are ignored so we do not delete target repo content.
for (const relPath of SYNC_PATHS) { for (const relPath of SYNC_PATHS) {
const src = path.join(workspace, relPath); const src = path.join(workspace, relPath);
const dest = path.join(repoDir, relPath); const dest = path.join(repoDir, relPath);
@@ -76,21 +76,12 @@ export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync)
fs.mkdirSync(path.dirname(dest), { recursive: true }); fs.mkdirSync(path.dirname(dest), { recursive: true });
fs.copyFileSync(src, dest); fs.copyFileSync(src, dest);
existingSyncPaths.push(relPath); existingSyncPaths.push(relPath);
continue;
} }
if (fs.existsSync(dest)) {
fs.rmSync(dest, { force: true });
}
missingSyncPaths.push(relPath);
} }
if (existingSyncPaths.length > 0) { if (existingSyncPaths.length > 0) {
run(['add', ...existingSyncPaths], repoDir); run(['add', ...existingSyncPaths], repoDir);
} }
if (missingSyncPaths.length > 0) {
run(['rm', '--cached', '--ignore-unmatch', '--', ...missingSyncPaths], repoDir);
}
const status = run(['status', '--porcelain'], repoDir); const status = run(['status', '--porcelain'], repoDir);
if (!status) { if (!status) {
+5 -5
View File
@@ -101,18 +101,18 @@ describe('commitAndPush', () => {
assert.ok(!addCall.args.includes('README.md')); assert.ok(!addCall.args.includes('README.md'));
}); });
it('removes missing sync paths from the repo copy', async () => { it('keeps repo copies when the source sync file is missing', async () => {
const missingPath = path.join(workspace, '.amazonq/rules/triage-findings.md'); const missingPath = path.join(workspace, '.amazonq/rules/triage-findings.md');
fs.rmSync(missingPath, { force: true }); fs.rmSync(missingPath, { force: true });
fs.rmSync(path.join(workspace, 'repo', '.amazonq/rules/triage-findings.md'), { force: true }); const repoPath = path.join(workspace, 'repo', '.amazonq/rules/triage-findings.md');
fs.writeFileSync(repoPath, 'stale');
const spawn = makeSpawn(); const spawn = makeSpawn();
await commitAndPush(workspace, path.join(workspace, 'repo'), spawn); await commitAndPush(workspace, path.join(workspace, 'repo'), spawn);
const rmCall = spawn.calls.find(c => c.args[0] === 'rm'); const rmCall = spawn.calls.find(c => c.args[0] === 'rm');
assert.ok(rmCall, 'expected git rm to run'); assert.equal(rmCall, undefined, 'git rm should not run for missing source files');
assert.ok(rmCall.args.includes('.amazonq/rules/triage-findings.md')); assert.equal(fs.readFileSync(repoPath, 'utf8'), 'stale');
assert.equal(fs.existsSync(path.join(workspace, 'repo', '.amazonq/rules/triage-findings.md')), false);
}); });
it('overwrites existing repo copies with workspace files', async () => { it('overwrites existing repo copies with workspace files', async () => {