console-ui:把「線上跑的是不是當代的」變成機械判準,並接進部署鏈

leo 2026-08-08:「已經發生過一次這個錯誤,把舊版界面上到 prod,
你要確定不可再犯。」

實測(repo 對線上,純文字資產比對):
  repo   portal/index.html  343,969 bytes
  線上   mira.uncle6.me      82,911 bytes(Songti 12 處,舊金色 serif 品牌)
  線上   pages.dev           82,911 bytes(同上)
而 e730b3f 那版 verify-live 對這兩站**三項檢查全過**——因為它驗的是
組態(apiBase、profile 的 views/home),不是世代。
⇒ 一個網址可以組態完全正確、卻對外展示一套早就被淘汰的介面,
  而所有機械檢查都說它是綠的。這就是要消滅的狀態。

本次落地:

一、世代指紋(targets.mjs)
  逐一取線上/產物的資產(index / portal / console / favicon.svg),
  遮掉本來就該隨部署目標不同的那兩行(VIEWS/HOME),其餘按位元組比對。
  刻意不用關鍵字清單——清單要人維護,而舊世代能無聲上線正是因為沒人記得維護它。
  誠實 trade-off 寫在檔內:repo 改了沒部署就會判紅,那是正確的(那時線上確實不當代)。

二、宣告值真的寫進產物(收掉 e730b3f 標的 WIP)
  deploy.mjs 改為由 targets.mjs 產出 .staging/<目標> 再推:
  config.js 由宣告值即時產生、console 的 VIEWS/HOME 依 profile 覆寫,
  **覆寫沒命中就中止部署**;推之前回頭讀磁碟上那份驗一次(不看腳本印了什麼)。
  public/config.js 刪除——它是產物不是原始碼。

三、修好一道從 08-03 起就在誤判的閘
  t160 的世代閘比對 portal 全文含「登記新庫」即拒部,而 66f1b59(08-03)
  加了一則**說明「已經把它拿掉了」的 HTML 註解** ⇒ 該閘自那天起每次誤判,
  npm run deploy:personal 連續五天推不出去。改成剝掉註解後只看可見內容,
  並降級為輔助(主判準是指紋)。這正是「手工關鍵字閘會腐爛」的實例。

四、讓它在該跑的時候真的被跑到(不再生出沒人記得執行的腳本)
  · deploy.mjs 推完自動回頭驗線上,不過就算本次部署失敗
  · .deploy-state.json 只在線上實測通過後才寫,且不進版控
    (新 checkout 沒紀錄=狀態未知=該被提醒,而不是繼承別人的綠燈)
  · Stop hook 每回合離線比對「手上這一代 vs 最後一次驗過的部署」,
    在要說「做完了」的那一刻出聲(實測 0.096s,不連網)

五、uncle6 邊界寫進工具本身(leo 08-08:「要看範例只在 youlin 網站,不要去碰 uncle6」)
  deploy.targets.json 的 enterprise 標 frozen:deploy 拒絕部署、verify 連抓都不抓。
  目標本身保留不刪——刪掉就變成下一個 AI 眼中「從來沒有過這個站」的失憶。
  同源清掉兩處還活著的舊記錄:README 的線上 demo 連結、public/index.html 的註解。

驗收證據見 commit 後的實測輸出(舊世代樣本取自 git 歷史 ad367e4,本機起站餵判準,
未碰任何線上資源)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-08-08 20:36:18 +08:00
parent e730b3f831
commit 42cb1d7aa9
11 changed files with 531 additions and 81 deletions
+37
View File
@@ -512,6 +512,41 @@ Workers Secret 也還在)。正解=刪除鈕標「即將開通」等 T9,**
---
## 25. 組態全綠,介面卻是舊世代——「驗了組態」被當成「驗了線上」(2026-08-08)
**leo 原話**:「已經發生過一次這個錯誤,**把舊版界面上到 prod,你要確定不可再犯**。」
**現場**:實測三個對外網址,`apiBase` / `profile.views` / `profile.home` **三項全過**
而它們跑的是 07-22 那一代的 portal82,911 bytes、舊金色 serif 品牌、`Songti` 12 處);
repo 早已是 343,969 bytes 的新品牌世代,`Songti` 一處不剩。
**一個網址可以組態完全正確、同時對外展示一套早就被淘汰的介面,而所有機械檢查都說它綠。**
**兩個根因,分開記**
1. **驗證的維度少了一個**。組態(連去哪、開哪幾頁)與世代(跑的是哪一版前端)是**兩件事**,
只驗前者會得到有害的綠燈——它讓人以為驗過了。
解:`console-ui/scripts/verify-live.mjs` 加第二層「世代指紋」=逐一抓線上資產、
遮掉本來就該隨部署目標不同的那兩行(VIEWS/HOME),**其餘按位元組比對 repo `public/`**。
位元組比對是刻意的:**不用關鍵字清單**——清單要人維護,而舊世代能無聲上線,
正是因為沒有人記得維護它。
2. **手工維護的關鍵字閘會腐爛,而且會反過來咬你**。t160 那道世代閘寫的是
「portal 全文含『登記新庫』就拒部」。08-03(`66f1b59`)有人在 portal 加了一則
**說明「已經把登記新庫拿掉了」的 HTML 註解** ⇒ 這道閘從那天起每次都誤判,
`npm run deploy:personal` 連續五天推不出去,而錯誤訊息說的是「你的 UI 是舊代」。
解:文字閘比對前先剝掉 HTML 註解(只看使用者看得到的內容),並降級成輔助——主判準是指紋。
**判準(下次照用)**:問「線上這一份**是不是我們手上這一份**」,
不要問「線上這幾個設定值對不對」。前者一句話涵蓋後者答不出來的東西。
**還有一半是「有沒有人記得跑」**。再好的檢查放在沒人執行的腳本裡等於不存在(這個 repo 已有數支那種)。
故三處接死:① `deploy.mjs` 推完自動回頭驗線上,不過就算本次部署失敗;
`.deploy-state.json` **只在線上實測通過後**才寫(不是跑過指令就寫);
③ Stop hook`stop-check-sync.sh`)每回合離線比對「手上這一代 vs 最後一次驗過的部署」,
在 CC 要說「做完了」的那一刻出聲。
---
## 快速檢查清單(做新功能前)
- [ ] 這是工作流還是零件?問「有必要嗎?」
@@ -531,3 +566,5 @@ Workers Secret 也還在)。正解=刪除鈕標「即將開通」等 T9,**
- [ ] 退役/降級某零件?同步清「AI 搜尋零件的三個源」=示例 yaml + parts.ts 硬編碼清單 + validate 跳過是設計);別只改一處宣布完成(#22
- [ ] 沒對應 recipe?誠實留 TODO + 發 issue 補 seed,別硬塞語意不符的 canonical_id 充數(假綠,#22
- [ ] 本地/Gitea 改完 code 想 `acr update` 部署?先確認:它抓的是 GitHub codeload tarball,不是你剛改的目錄(#23
- [ ] 改完前端說「做完了」?先問**線上跑的是不是這一份**(`cd console-ui && npm run verify`)——組態綠不代表世代對(#25
- [ ] 要寫「含某關鍵字就擋」的閘?先想「有人寫一則說明它已被移除的註解時會怎樣」——關鍵字閘會腐爛,優先用指紋(#25