Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c99faec
feat(scripts): 002 記分守門移植進 main——pre-commit 第 6 步+CI 聚合強制(#881)
Jul 26, 2026
b60d935
docs: 002 記分守門文件與 main 實況對齊+新增 AGT-LOG-03 單一提交規則(#881)
Jul 26, 2026
668bd3a
fix(scripts): 002 守門 pre-commit 補 staged 刪除防護(#884 review P2)
Jul 26, 2026
666ef82
docs: changeset 適用界線與 002 補記時機明確化(#884 review P1)
Jul 26, 2026
2080c87
fix(scripts): 002 守門 symlink 直跑判定與錯誤定位修正(#884 review round 2)
Jul 26, 2026
0b3b634
fix(scripts): 002 守門空白 ID 視同缺少 ID(#884 review round 3)
Jul 26, 2026
295b242
fix(scripts): 002 守門拒絕空白的原因與解法欄位(#884 review round 4)
Jul 26, 2026
e6d7adc
fix(scripts): 002 守門禁止掏空既有條目欄位(#884 監控席回報)
Jul 26, 2026
3aa1585
test(scripts): 補既有條目整行刪除的掏空回歸鎖(#884 監控席回報)
Jul 26, 2026
6151b2e
fix(ci): 002 守門補 hook 觸發面與 main push 兜底+條目區段唯一性(#884 監控席回報)
Jul 26, 2026
62faa30
fix(ci): 002 守門 main push 基準改直取 commit+檔頭正負號強制(#884 review round 5)
Jul 26, 2026
bb0cf9a
fix(scripts): 002 守門 CASCADE fail-closed+雙 flag 互斥+文件逐句對齊(#884 終審)
Jul 26, 2026
f42db6a
fix(scripts): 002 守門 gitShow 契約閉合+唯一性範圍收斂+全 repo 註解對齊(#884 複審)
Jul 26, 2026
5f82b8d
fix(scripts): 002 守門 gitShow 環境失敗改 fail-closed+catch 逐一稽核(#884 終審)
Jul 26, 2026
450029b
fix(scripts): 002 守門鎖定 git 輸出 locale(#884 最終複審)
Jul 26, 2026
0001734
fix(scripts): 002 守門存在性判定改結構化探測,不再比對 git fatal 訊息(#884)
Jul 26, 2026
ce96aad
fix(scripts): 002 守門 orphan HEAD fail-closed+結構鎖升級 AST(#884)
Jul 26, 2026
32e11e8
chore(starpuff): #884 收斂輪活性錨
Jul 26, 2026
054325f
fix(scripts): 002 守門無基準判準升級三層結構探測(#884)
Jul 26, 2026
fc0914a
test(scripts): 002 守門 AST 結構鎖收斂間接繞法並補 mutation 元測試(#884)
Jul 26, 2026
9666bfc
fix(scripts): 002 守門補 unrelated histories 診斷與措辭鎖自我偵測(#884)
Jul 26, 2026
36fef71
test(scripts): 002 守門補 mutation 覆蓋缺口與能力邊界出界(#884 複審)
Jul 26, 2026
d42c335
docs: 002 聚合記錄守門移植與收斂全輪(#881/#884)
Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ jobs:
node-version: '24'
cache: 'pnpm'

# 002 記分守門 CI 端強制(issue #661):驗 PR 最終態 vs merge-base,堵 --no-verify 與網頁端 merge 繞過。
# 002 未變更時即刻跳過;與 pre-commit 共用 validate002 核心,零 npm 依賴故置於 install 前搶先紅燈。
- name: Verify 002 log (vs merge-base)
if: github.event_name == 'pull_request'
run: node scripts/verify-002-log.mjs --base-ref "${{ github.event.pull_request.base.sha }}"
Comment thread
s123104 marked this conversation as resolved.

# main 直推兜底:守門不假設 branch protection 永遠有效——這道守門的存在理由就是防止
# 稽核證據被湮滅。基準取 github.event.before(本次 push 前的 main tip),可涵蓋一次推多個
# commit;merge commit 的 before 即第一父,語意等同「main 上這次多了什麼」。
# 用 --base-commit 而非 --base-ref:force push 時 before 不是 HEAD 的祖先,
# 取 merge-base 會退到更早的共同祖先,讓被改寫掉的條目驗不出來。
# 分支初建後 before 為全零無法解析,該情境跳過而非誤紅。
- name: Verify 002 log (main push)
if: github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000'
run: node scripts/verify-002-log.mjs --base-commit "${{ github.event.before }}"

- name: Install dependencies
run: pnpm install --frozen-lockfile

Expand Down
25 changes: 18 additions & 7 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@
echo "執行 Pre-commit 檢查..."

# 1. Lint-staged (程式碼格式化與 Lint)
echo "\nStep 1/5: Lint-staged..."
echo "\nStep 1/6: Lint-staged..."
pnpm lint-staged
if [ $? -ne 0 ]; then
echo "\nLint-staged 失敗,請修復後再提交"
exit 1
fi

# 2. TypeScript 檢查 (快速驗證類型錯誤)
echo "\nStep 2/5: TypeScript 檢查..."
echo "\nStep 2/6: TypeScript 檢查..."
pnpm typecheck
if [ $? -ne 0 ]; then
echo "\nTypeScript 檢查失敗,請修復類型錯誤後再提交"
exit 1
fi

# 3. 格式檢查 (確保所有文件已格式化)
echo "\nStep 3/5: Prettier 格式檢查..."
echo "\nStep 3/6: Prettier 格式檢查..."
pnpm format
if [ $? -ne 0 ]; then
echo "\n格式檢查失敗,請執行 'pnpm format:fix' 後再提交"
Expand All @@ -36,26 +36,37 @@ fi
# 4. SSOT 同步驗證 (確保 SEO 配置一致性)
# [可選] 僅在 ratewise 相關檔案有變更時執行
if git diff --cached --name-only | grep -qE "(seo-paths|app\.config)"; then
echo "\nStep 4/5: SSOT 同步驗證..."
echo "\nStep 4/6: SSOT 同步驗證..."
node scripts/verify-ssot-sync.mjs 2>/dev/null
if [ $? -ne 0 ]; then
echo "\nSSOT 同步驗證失敗,請確保 seo-paths.config.mjs 與 seo-paths.ts 一致"
exit 1
fi
else
echo "\nStep 4/5: SSOT 同步驗證 (跳過 - 無相關變更)"
echo "\nStep 4/6: SSOT 同步驗證 (跳過 - 無相關變更)"
fi

# 5. 版本 SSOT 驗證 (確保版本管理不繞過 SSOT)
if git diff --cached --name-only | grep -qE "(apps/ratewise/src|apps/ratewise/package\.json|package\.json)"; then
echo "\nStep 5/5: 版本 SSOT 驗證..."
echo "\nStep 5/6: 版本 SSOT 驗證..."
node scripts/verify-version-ssot.mjs
if [ $? -ne 0 ]; then
echo "\n版本 SSOT 驗證失敗,請依照訊息修正"
exit 1
fi
else
echo "\nStep 5/5: 版本 SSOT 驗證 (跳過 - 無相關變更)"
echo "\nStep 5/6: 版本 SSOT 驗證 (跳過 - 無相關變更)"
fi

# 6. 002 記分守門 (issue #608:檔頭記分與新增條目自動對帳)
# 無條件執行:以 git diff 判斷是否觸發會被 `git mv` 繞過(--name-only 只列新路徑,
# 舊路徑不出現),守門不應依賴 diff 呈現方式。跳過與否由腳本自行以 index 與 HEAD
# 的存在性決定;開銷遠低於同一 hook 內的 pnpm typecheck,對體感無影響。
echo "\nStep 6/6: 002 記分守門..."
node scripts/verify-002-log.mjs
if [ $? -ne 0 ]; then
echo "\n002 記分守門失敗,請依照訊息修正檔頭記分與條目"
exit 1
fi

echo "\n所有 Pre-commit 檢查通過"
131 changes: 113 additions & 18 deletions AGENTS.md

Large diffs are not rendered by default.

34 changes: 23 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ pnpm format:fix # prettier --write .

## Execution SOP(AI 助手執行程序)

| Phase | 核心動作 |
| ----------------- | ----------------------------------------------------------------------------------- |
| **1. Intake** | 確認目標、輸出物、風險等級(commit/push/merge?CI/PWA/版本?) |
| **2. Context** | 讀最小必要檔案;flow 變更先讀 `package.json` / `.husky/*`;合併前 `gh pr status` |
| **3. Evidence** | build error / 新工具 / CI 變更 / major 升級 → **先查官方文件**(Context7) |
| **4. Execution** | 最小必要變更;禁止跨 app 無關修改;保持可回滾;勿刪未追蹤 `.agents/skills/*` |
| **5. Validation** | 文檔→SSOT 一致;程式碼→typecheck/test/build:ratewise;UI→截圖+console errors |
| **6. Commit** | 更新 `docs/dev/002...`(對齊當前 SSOT 模板 + 分數變化 + 累計總分)→ commitlint 提交 |
| **7. Release** | 見下方 Phase 7 版本發布流程 |
| Phase | 核心動作 |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| **1. Intake** | 確認目標、輸出物、風險等級(commit/push/merge?CI/PWA/版本?) |
| **2. Context** | 讀最小必要檔案;flow 變更先讀 `package.json` / `.husky/*`;合併前 `gh pr status` |
| **3. Evidence** | build error / 新工具 / CI 變更 / major 升級 → **先查官方文件**(Context7) |
| **4. Execution** | 最小必要變更;禁止跨 app 無關修改;保持可回滾;勿刪未追蹤 `.agents/skills/*` |
| **5. Validation** | 文檔→SSOT 一致;程式碼→typecheck/test/build:ratewise;UI→截圖+console errors |
| **6. Commit** | 更新 `docs/dev/002...`(對齊當前 SSOT 模板 + 分數變化 + 累計總分;條目集中單一 commit)→ commitlint 提交 |
| **7. Release** | 見下方 Phase 7 版本發布流程 |

### 002 格式與獎懲分數 SSOT(對齊 `AGENTS.md`)

Expand All @@ -104,7 +104,17 @@ pnpm format:fix # prettier --write .
- `neutral = 0`
- `本次分數變化 = reward_count - penalty_count`
- `最新總分 = 前次總分 + 本次分數變化`
- 每次 commit 前新增 002 紀錄時,必須同步更新「本次分數變化」與「累計總分」。
- 新增 002 紀錄時,必須同步更新「本次分數變化」與「累計總分」;條目累積後於單一 commit 落盤(`AGT-LOG-03`)。
- 檔頭記分行固定格式:`> 本次分數變化:+N(reward a、penalty b、neutral c)|累計總分:+T`;條目 ID 必須以 `reward-` / `penalty-` / `neutral-` 開頭。
- `pre-commit` 第 6 步(`scripts/verify-002-log.mjs`,**無條件執行**,跳過與否由腳本自行判定)自動驗證記分:`a+b+c` = 本次新增條目數、`N = a - b`、`T` = 前版累計 + `N`、四行模板、歷史條目不可刪除(issue #608);staged 刪除整份 002(`git rm`)亦必紅。ID 唯一性只對**本次造成的重複**擋 commit(歷史既有重複不回溯,否則會卡死所有後續 commit);刪除比對只採信基準版解析結果,區段外的獨立 `- ID:` 行不列入。「無基準版」只能由三層結構探測證明——`git rev-list -n 1 --all --reflog` 為空且 object store 無 commit 物件(`checkout --orphan` 會讓 `rev-parse --verify HEAD` 失敗、orphan 後刪光 named refs 會讓 `--all` 為空,但歷史 commit 仍在,誤判即跳過刪除防護);存在性判定用 `ls-files`/`ls-tree`(不存在 = exit 0 空輸出)而非 `cat-file -e` 的 fatal 訊息比對——後者把「不存在」與「repo 不可用」混在同一離開碼、只能比對英文訊息,已連續破三次;所有 git 子行程收斂在唯一帶 `LC_ALL=C` 的 wrapper。**基準版不可解析、或讀不出前版累計總分時一律 fail-closed**(否則先 poison tip 再清空歷史即可全綠);僅在基準版不可解析時,刪除比對才退回全檔原始文字掃描 `- ID:` 作第二道保險。
- 既有條目的欄位不可被掏空:基準版非空的欄位(日期/原因/解法)改後不得為空。**掏空=就地刪除**故擋;**改寫不擋**,因為它與合法的精確性修正(改錯字、更正數字)無法機械區分,擋下會封死唯一的更正管道。判準是「有沒有從有變成無」,不是「內容有沒有變」;語意品質交由審查把關。詳見 `AGENTS.md` § 為什麼堵「掏空」但不堵「改寫」。
- pre-commit 第 6 步無條件執行、不以 `git diff` 判斷觸發(`git mv` 的 `--name-only` 只列新路徑會繞過);條目區段 `## 條目` 必須唯一(多個等於替後續區段開永久盲區)。
- `git merge` 的 merge commit 走 `pre-merge-commit` 而非 `pre-commit`,本 repo 未設前者故 hook 層不覆蓋——刻意不補(會讓 `git merge origin/main` 誤紅),由 CI 兜底。
- CI `Quality Checks` 於 install 前跑守門(issue #661):PR 事件用 `--base-ref <base sha>`,基準取 `merge-base(base, HEAD)`;**main push 事件用 `--base-commit <github.event.before>`**(全零時跳過),基準**直取該 commit 不走 merge-base**——force push 時 `before` 並非 HEAD 祖先,取 merge-base 會漏驗被改寫的條目。兩個 flag 互斥。守門不假設 branch protection 永遠有效。pre-commit 只看單一 commit,squash 聚合的檔頭記帳錯誤(逐 commit 各自合法、聚合後淨變化不符)只有 CI 端攔得到。
- **一個 PR 的 002 條目必須集中在單一 commit**(`AGT-LOG-03`),檔頭直接寫 PR 聚合淨變化。在現行兩種語意下,002 分散多個 commit 時 pre-commit(逐 commit 對帳)與 CI(聚合對帳)互斥,無法同時綠燈。
- 此為設計取捨非技術必然:已評估「pre-commit 也改用 `merge-base(<base>, HEAD)` 聚合語意」(可行、能保留逐 commit 寫法),因 **base 不恆為 main**(實驗線 PR base 指向 experiment 分支,硬寫 `origin/main` 會產生假紅/假綠)、本機無權威 base 來源,而 CI 已有零猜測的 `base.sha` 而不採用。詳見 `AGENTS.md` § `AGT-LOG-03` 已評估但不採用的替代方案。
- 002 落盤後的審查修正 commit 不得新增條目(pre-commit 必紅);補記併回同一個 002 commit——仍在 tip 用 `git commit --amend`,否則延到 rebase 時 fold。故盡量讓 002 commit 留在分支最後。
- rebase 解 002 衝突後,`git rebase --continue` 不觸發 pre-commit——必須手動執行 `node scripts/verify-002-log.mjs` 驗證,或事後以 `git commit --amend` 重新觸發守門。

### Phase 7. 版本發布與依賴管理(Release & Dependencies)

Expand All @@ -128,8 +138,10 @@ pnpm format:fix # prettier --write .
- 新互動元件(MoneyBox 比較卡、星評 Modal)→ **minor**
- Core Web Vitals 架構性改善(LCP ↓50%+,SSG 預渲染)→ **minor**

**Changeset 規範**(每個 PR 完成後 MUST 執行 `pnpm changeset`):
**Changeset 規範**(**有 package 變更**的 PR 完成後 MUST 執行 `pnpm changeset`):

- 適用界線:changeset 的對象是 package。變更落在 `apps/*/**`(含該 app 的 `docs/`、`README.md`)→ **要**;純 root 層變更(`scripts/`、`.husky/`、`.github/`、root 設定與文件、`docs/dev/*`)→ **不要**,因為沒有任何 package 可 bump
- 判斷依據是變更檔案所屬 package,不是 commit type
- bump 類型選正確(見上表);描述使用者**看得到**的影響,禁止描述實作細節
- CHANGELOG 由 changeset 自動生成,禁止手動貼入 git log
- commit 數量不等於升版次數;`.changeset/*.md` 是 release intent,`pnpm changeset:version` 才會消化成版本與 CHANGELOG
Expand Down
Loading
Loading