Files
tea-sdlc/test/sdlc-sync-assets.test.js
T
jiantw83 767ca69c12 test(流程正本): 新增 sdlc-sync,並讓另外兩份正本整併完自動接回
挑出哪些留言真的是決策、寫回去之前讓使用者點頭、略過的保持未標記——三件都只有正本做得到。

analyze 與 feat 原本寫「建議先執行 /sdlc-sync,再回來」,那等於要使用者重打指令。改成直接走
sync 的流程、做完自動接回,並在接回前重新抽取一次——接著用舊的那一份做事,這一整段就白做了。

略過的留言會讓未處理留言數停在大於 0,於是 analyze 與 feat 每次都會再停一次。這是驗收標準
本身的兩條放在一起的結果,正本把它講明白,讓使用者分得出「沒整併乾淨」與「我選了略過」。
2026-09-17 09:08:09 +00:00

152 lines
6.7 KiB
JavaScript

/**
* /sdlc-sync 的流程正本,以及另外兩份正本的「接回」那一段。
*
* 這個指令的價值在於「描述不再騙人」,而三件關鍵事只有正本做得到:挑出哪些留言真的是
* 決策、寫回去之前讓使用者點頭、以及略過的那幾則要保持未標記。寫漏任何一件,
* 描述就會繼續過期,或者有決策被靜靜吞掉。
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { assertNeutralPrompt, readPrompt } from './helpers/prompt-doc.js';
const prompt = readPrompt('sdlc-sync');
const steps = prompt.slice(prompt.indexOf('## 1.'), prompt.indexOf('## 邊界'));
test('正本平台中立,description 前綴正確', () => {
assertNeutralPrompt(prompt, 'sdlc-sync');
});
test('兩種議題各自指名對應的抽取腳本', () => {
assert.match(steps, /issue-extract/);
assert.match(steps, /wp-extract/);
assert.match(steps, /需求議題.*工作包議題|工作包議題.*需求議題/s);
});
test('留言內容另外拿,並說明為什麼抽取契約不夠', () => {
assert.match(steps, /pr-comments/);
assert.match(steps, /只給數字不給內容/);
});
test('說明那支腳本議題與 PR 都收得下,不再宣稱「PR 也是 issue」', () => {
assert.match(steps, /議題與 PR 都收得下/);
assert.equal(steps.includes('Gitea 的 PR 也是 issue'), false, '反過來說才對:每個 PR 都是議題');
});
test('已處理認的是自己打的 +1', () => {
assert.match(steps, /自己打的/);
assert.match(steps, /我同意/);
});
test('略過會讓計數停在大於 0,這件事要講明白', () => {
const section = steps.slice(steps.indexOf('## 5.'));
assert.match(section, /停在大於 0/);
assert.match(section, /每次開始時都會再停一次/);
assert.match(section, /他選了略過/, '要讓使用者分得出「沒整併乾淨」與「選了略過」');
});
test('已標記過的留言跳過', () => {
assert.match(steps, /已經標記過的留言不再處理|已處理.*跳過/s);
});
// ── 挑決策 ─────────────────────────────────────────────────────────
test('要整併與不整併各有判斷依據,不是只給兩個詞', () => {
assert.match(steps, /\*\*要整併\*\* — .{10,}/);
assert.match(steps, /\*\*不整併\*\* — .{10,}/);
});
test('判斷不了時當成要整併,並說出漏掉的代價', () => {
assert.match(steps, /判斷不了的\*\*當成要整併\*\*/);
assert.match(steps, /描述就會繼續騙後面的人/);
});
// ── 先點頭再寫 ─────────────────────────────────────────────────────
test('寫回去之前要列出方案給使用者看', () => {
assert.match(steps, /先列出來再動手/);
assert.match(steps, /哪一段/);
assert.match(steps, /改完長什麼樣/);
});
test('一次問一題,且略過的會保持未標記', () => {
assert.match(steps, /一次問一題/);
assert.match(steps, /略過的留言保持未標記/);
assert.match(steps, /下次執行還會被提出來/);
});
test('說明了這一步是描述的最後一道關卡', () => {
assert.match(steps, /最後一道關卡/);
assert.match(steps, /拿它當事實/);
});
// ── 寫回 ───────────────────────────────────────────────────────────
test('指名 comments-merge,並要求先試跑', () => {
assert.match(steps, /comments-merge\.js/);
assert.match(steps, /--dry-run/);
});
test('--content-file 的內容是整段,且說明標題那一行不含在內', () => {
assert.match(steps, /那一段改完的完整內容/);
assert.match(steps, /不含 `## 標題` 那一行/);
});
test('--merged 只放真的併進去的,並說出放錯的後果', () => {
assert.match(steps, /只放\*\*真的被併進這一段\*\*/);
assert.match(steps, /再也\n?不會被提出來|再也不會被提出來/);
});
test('SECTION_NOT_FOUND 的處置是回頭確認,不是硬塞別的段落', () => {
assert.match(steps, /SECTION_NOT_FOUND/);
assert.match(steps, /不要改用別的段落硬塞/);
});
// ── 回報 ───────────────────────────────────────────────────────────
test('回報要列出略過的那幾則', () => {
const section = steps.slice(steps.indexOf('## 5.'));
assert.match(section, /略過的那幾則是哪些/);
assert.match(section, /下次還會出現/);
});
// ── 接回原本的指令 ─────────────────────────────────────────────────
test('整併完自動接回,不要求使用者重打指令', () => {
const section = prompt.slice(prompt.indexOf('## 接回原本的指令'));
assert.match(section, /直接接回去/);
assert.match(section, /不要要求使用者重打一次/);
});
test('接回之前要重新抽取,並說明為什麼', () => {
const section = prompt.slice(prompt.indexOf('## 接回原本的指令'));
assert.match(section, /先重跑一次抽取/);
assert.match(section, /這一整段就白做了/);
});
// ── 邊界 ───────────────────────────────────────────────────────────
test('邊界列出不做的事,含不刪改留言', () => {
const boundary = prompt.slice(prompt.indexOf('## 邊界'));
assert.match(boundary, /不自行決定要不要整併/);
assert.match(boundary, /不整份重寫描述/);
assert.match(boundary, /不標記略過的留言/);
assert.match(boundary, /不刪除、不編輯任何留言/);
assert.match(boundary, /誰說過什麼的紀錄/, '要說出為什麼留言不能動');
});
// ── 另外兩份正本的接回 ─────────────────────────────────────────────
for (const name of ['sdlc-analyze', 'sdlc-feat']) {
test(`${name} 偵測到未整併留言時會轉去整併,並自動接回`, () => {
const other = readPrompt(name);
assert.match(other, /未處理留言數/);
assert.match(other, /直接走 `\/sdlc-sync` 的流程/, `${name} 要真的轉過去,不是叫人自己跑`);
assert.match(other, /自動接回這裡/);
assert.match(other, /不要要求使用者重打指令/);
});
test(`${name} 接回前要重新抽取一次`, () => {
assert.match(readPrompt(name), /重新抽取一次拿到更新後的描述/);
});
}