系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。
我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。
本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。
Change Ref: serialwrap PR #105-docs: 說明文件對齊現行設計(CLAUDE.md/README,除 spec)
Follow-up Ref: paulsha-conventions PR #27-feat: #26 文件規則補強(doc_paths + R-25 doc_coverage + R-26 generated_facts)
Issue: serialwrap 的 Runtime、CLI 與 Agent 操作方式早已更新,但 README、CLAUDE、Skill 與部分 Design Docs 仍保留舊路徑、舊命令與退役介面。
Root Cause: Runtime Change 有 Code、Test 與 PR 承接;文件更新卻仍依賴人記得。同一個工程事實散落在多份文件裡,久了便形成數個彼此不一致的 Authority。
Solution: 重新盤點 Agent 會讀取的現行文件,修正路徑、命令與架構描述,並把退役設計明確標記為歷史快照。
Evidence: 共修正 7 個文件/Help 相關檔案;Tests、Package 與既有 Policy Workflow 全部成功。
今天的故事其實是這樣開始的:
月底,我依慣例打開 Claude 好朋友。手頭拮据的我,想著只是跑個測試,用不著 Opus 大哥出馬吧?
於是我派了小老弟 Haiku,想讓它先把 DUT/STA 的環境架好:
幫我用 serialwrap,把 COM0 DUT 跟 COM1 STA 用 Wi-Fi 連起來。
起身、泡咖啡、去找隔壁夥伴聊聊天,再蹲個廁所當一下薪水小偷。
半個小時後回來,螢幕上的 Haiku 這樣告訴我:
tmux send-keys 找不到 minicom
讓我看看 serialwrap 要怎麼用
啟動 minicom 連線 /dev/ttyUSB0
連線失敗,讓我看看發生什麼事了
/dev/ttyUSB0 被背景應用程式 serialwrapd 佔住了,
讓我先停掉背景程式
背景程式已停止,
minicom 可以連線 /dev/ttyUSB0 了
啟動 serialwrap
serialwrap 啟動失敗,
我來查清楚發生什麼事
serialwrap 沒有送出 tmux send-keys,
讓我來修正這個問題
……
……
……
昏倒!!!
哈囉~
serialwrap 不用 send-keys 很久了好嗎?
冷靜下來後,我本來想先把鍋扣在模型頭上。
畢竟 Haiku 嘛。
便宜、快速,做錯事時還會非常勤奮地繼續做下去。
但我把它剛才的操作一路往回翻,才發現:
欸。
這些東西好像不是它自己掰的。
tmux 不是它憑空想到的。
「停掉 serialwrapd,直接搶 /dev/ttyUSB0」這種復古玩法,也不是它臨場自由發揮。
它有先 Survey Repo。
然後非常認真地照著裡面的文件做。
而 Repo 裡確實還留著:
舊的 tmux/minicom 操作方式
舊的 ~/.paul_tools/minicom 入口
舊的 /tmp/serialwrap State Path
舊的 daemon 啟動命令
舊的 Unix Socket 路徑
已經退役的 MCP 說明
Haiku 只是拿到一份年代不明的操作手冊,然後從第一頁一路做到最後一頁。
以前是 minicom 直接持有實體 TTY,serialwrap 再透過 tmux 對 minicom send-keys。
現在則是 serialwrapd 直接持有 UART,Human Console 與 Agent 都必須經過 Broker。
Haiku 看到:
/dev/ttyUSB0 is busy
holder: serialwrapd
腦中的架構如果還停在舊版,下一步自然就是:
serialwrapd 擋住 minicom 了,先停掉它。
照舊文件看,沒什麼毛病。
照現在的架構看,它正在把已經重構好的 serialwrap,一步一步拆回舊版。
Agent 不一定會看不懂過期文件。
有時候剛好相反。
它會理解一套早就不存在的系統,然後很有自信地開始改回舊架構。
我把 Haiku 剛才碰過的文件全部攤開,逐條對照現在的 Runtime。
CLAUDE.md 少了 SerialPort Abstraction、Windows TCP Console 與多 daemon 偵測;README.md 的 WAL/State 還指向舊的 /tmp/serialwrap/,Human Console 也仍使用 ~/.pub_tools/minicom。
systemd 的入口早就改成:
serialwrap service restart
文件裡卻還留著:
serialwrap daemon start
在 systemd 模式下直接跑 daemon start,可能再起一個未受監管的 serialwrapd。
同一條 UART 前面多出一個 Reader,掉字、Session 狀態亂飄,接下來大概又可以再寫三篇。
PR #105 先把文件拉回現況。
CLAUDE.md 補架構與操作慣例;README.md 修正 XDG State Path 與 Service 操作;內建 Skill 修正 Remote Support Socket。
舊 Design Docs 則標成「歷史快照」,再指向現行 README 與 Spec。MCP 也明確註記退役,免得下一個 Agent 以為只是少裝一個 Adapter。
PR #105 沒改 Runtime Code。下一個 Agent 至少不會一看到 /dev/ttyUSB0 被佔用,就先把真正負責 UART 的 daemon 殺掉。
這批 Drift 不是某一個 PR 一口氣寫壞的,而是一次漏一個 Path、一次少改一份文件,慢慢堆出來的。
這次剛好有 Haiku 幫我把古法完整演示一次。
下次沒有這麼熱心的演員呢?
文件同步,包含 Test 到底有沒有真的跑,在 Agent 開發的過程中已經困擾我很久了。
這次也算抓到機會,好好治治老毛病!!!
靈感來自 .github。
GitHub 帳號底下可以建立一個叫做:
.github
的 Repo。
它替帳號底下的 Repositories 提供 PR Template、CONTRIBUTING.md、SECURITY.md 等共用預設。其他 Repo 沒有自己的版本時,就先套用這裡的內容。
我看到這個設計,第一個想法是:
GitHub 都知道同一套規矩,不應該每個 Repo 各寫一次。
那我為什麼還在每個專案裡反覆提醒 Agent:
改 Code 記得同步文件
新增 Test 記得接進 CI
不要直接 Commit 到 main
PR 記得更新 Changelog
做完不要自己宣布成功,先把驗證跑完
每個 Session 重講一次,AGENTS.md 再包一次,還要看 Agent 記不記得、要不要執行。
問題不只在 Agent 的記性。
我根本沒有替這些規則建立 Authority。
所以我開始想:
.github
負責帳號層級的共用入口與預設文件
那是不是還可以再有一個中央 Repo
負責所有專案共同遵守的工程規則?
後來這個 Repo 就叫 paulsha-conventions,把散落在 PR Template、Agent Instructions、README,還有我腦袋裡的規矩,整理成可以版本化的 Repository Contract。
每個 Repo 宣告規則版本、Code 範圍、文件 Authority,以及 Test、Build、Release 要求。Agent 進 Repo 先讀 Instructions,交件前照 Checklist 檢查,再由 CI 執行這些規則的檢查: policy_check。
GitHub 已經替 Community Health Files 做過一次,我只是把同一個概念往 Agent 開發再推一步。
conventions 剛開始沒有二十幾條 Rule。
我先挑答案明確的問題:README、CHANGELOG、VERSION 在不在?Branch 有沒有亂開?Code 改了,Changelog 有沒有動?Repo 有 Tests,CI 到底有沒有跑?
跟這篇最直接相關的,先是 R-18。
Code Path 有變,README 或 Docs 完全沒動,它會 WARN。
再往前一步是 R-22。
Path、Function 或 Class 已經搬走,文件還指著舊位置,本次新破壞 FAIL,陳年問題先 WARN。
跑一次:
python3 -m policy_check --repo .
結果可以重播。同一份 Repo、同一版 Policy,誰來跑都應該拿到同一個答案。
看起來可行。
我再把 serialwrap 這次的狀況丟進去:
pass: 21
fail: 0
warn: 1
嗯?
Haiku 都已經把 Windows 11 拆回 Windows 3.1 了。
Policy Check 覺得整體健康。
R-18 只知道 Code 變了,README 或 docs/** 有沒有一起變。
R-22 很會抓已經消失的 Path 或 Symbol。
但這次留下來的是:
/tmp/serialwrap/serialwrapd.sock
~/.pub_tools/minicom
serialwrap daemon start
它們沒有死。
只是過期。
真正給 Agent 使用的 CLAUDE.md 在 Repo Root,內建 Skill 在 sw_core/assets/skill/SKILL.md,也不在原本的掃描範圍裡。
Rule 很老實地回答了我原本問它的問題。
只是我還沒問:
新增的修改,有沒有寫進該寫的文件?
PR #27 補了三件事。
doc_paths 讓 Repo 自己宣告 Canonical Docs。README、docs/**、CLAUDE.md、內建 Skill,誰具有 Authority,不再由 Rule 猜。
R-25 doc_coverage 從 Code 抽出 Module、RPC Method、Environment Variable 與 CLI Command Tree。新增了 serial_port.py,文件完全沒提到,就列出來。
R-26 generated_facts 則處理能由 Command 產生的內容。CLI Tree、版本號等結構化清單放進文件 Marker,CI 重跑一次,對不上就 FAIL。
這幾條 Rule 還是不會讀懂文章。
文件有寫 serial_port.py,內文卻還在講舊架構,它未必看得出來。這種仍要靠 Human 或 LLM Review。
但程式能確定的部分,先別再丟回人腦裡。
Policy Check 能擋 Merge,但 Agent 可能已經 Push、開完 PR,才看到 Policy、Tests 或 OpenSpec FAIL。
有些 Rule 還會執行 Repo 宣告的 Command,例如 CLI Help、CLI Tree、Generated Facts。Test 與 Build 也一樣。
這些不是無害字串。陌生 Fork 或不可信 Branch 裡的 Command,不能直接跑。
所以 conventions 後來又長出本地端的 preflight-ci。
Agent 準備 Claim Done 前,先依 Target Repo 的 Manifest 選定版本,跑 Policy、OpenSpec 與 Tests。
完成修改
→ 準備 PR Metadata
→ 本地 preflight-ci
→ PREFLIGHT PASS
→ Push/開 PR
→ Remote CI 再驗一次
它不解析 GitHub Actions 猜流程,也不臨時抓最新 Engine。執行版本與驗證步驟由 Target Repo 宣告,輸出也不能把 PR Metadata、Token 或 Secret 印滿 Terminal。
GitHub CI 仍是最後一道門。
preflight-ci 只是不要讓 Agent 走到門口,才發現自己褲子沒穿。
Rule 後來一路長到 R-26,依責任分成五組:
| 類別 | Rules | 管理範圍 |
|---|---|---|
| Repository 基本結構與版本 | R-01~R-08 | README、CHANGELOG、VERSION、SemVer、Release Tag 與 Policy Config |
| Change 與 PR 流程 | R-09~R-12、R-17 | Changelog、PR Title/Checklist、Branch Flow 與 Issue 關聯 |
| Agent、CI 與 Engine Authority | R-13~R-16、R-19、R-20、R-23 | Agent Instructions、Workflow Pin、CLI Help、Tests 進 CI、Policy Engine 版本 |
| Security Boundary | R-21 | 機密標記、個人路徑、Credential Pattern 與 Repo Tier |
| 文件與知識同步 | R-18、R-22、R-24~R-26 | Docs 同步、懸空引用、MOC、Doc Coverage 與 Generated Facts |
它們沒有讓 Agent 變聰明,只是把常被忘掉的事情,從 Prompt 和人腦搬進可以執行的 Gate。
以前文件寫錯,新人可能會跑來問:
這個 Command 怎麼不能跑?
現在 Agent 有 Shell、有 Git,也有權限停 Service。
它不一定會問,可能直接把 Code 改到符合文件。
所以 Agent 會讀,而且會照著執行的文件,已經不只是參考資料。
它會參與 Runtime。
PR #105 先把 Windows 3.1 的說明書換掉。
conventions 則負責提醒我,下次升級 Windows 12 時,不要又把說明書忘在上一版。
第一幕到這裡,Agent 已經能碰 UART,也不至於拿著古籍拆系統。
下一個問題換到 TestPilot。
Case YAML 有了,流程跑完了,Report 還寫著 PASS。
那個 PASS,到底憑什麼成立?
下一篇進入 Audit。
Have a nice day.
Reference: paulsha-conventions