工程原則
本頁列出從此儲存庫的完整問題/討論歷史(569 個問題、89 個討論,截至 v0.9.10)歸納出的設計原則、限制與護欄。每項原則都會引用促成該原則的事件。實作新功能與修正前,應先依照本頁進行檢查。
整段歷史中最重要的一項教訓是:幾乎每一個關鍵(P0/P1)錯誤,都是多寫入者路徑中的資料完整性錯誤——同步與介面、同步與自身、應用程式與 MCP/CLI、快照與累加式儲存之間的問題。介面錯誤很多,但修正成本低;寫入路徑錯誤則會讓使用者失去資料與信任。
0. 產品哲學(任何工程決策前都要檢查)
Mindwtr 的原則是預設簡單,需要時強大:漸進式揭露(進階選項在需要時才顯示)、預設從簡(更少欄位、更少旋鈕、更少干擾),以及不讓功能無止境膨脹(清晰勝過雜亂)。我只想騎腳踏車時,別給我一整個駕駛艙。
自動優於手動——推斷,不要詢問
如果能從平台、安裝管道、現有資料或情境自動判斷正確結果,應用程式就應直接處理。這適用於所有事物,不只設定:旋鈕、確認提示、工作流程中的額外點按,或必須由使用者操作的介面控制項,都是同一種失敗——應用程式提出了它本可自行回答的問題。每一個手動步驟,都把認知負擔從我們身上(一次)移到每位使用者身上(永遠)。
- **更新檢查:**一位使用者要求提供「停用更新檢查」切換開關,因為應用程式不斷向透過套件管理器安裝的版本提供 GitHub 下載。解法並不是新增開關:應用程式現在會偵測安裝管道,並依各管道正確運作——有正式資訊來源的管道會固定顯示對應管道的提醒,自行管理更新的管道則完全保持安靜。最終沒有加入任何設定。
- **#832:**全域快速新增快捷鍵的預設值會遵循各平台慣例(在 macOS/Linux 上以安全的組合鍵啟用;在全域快速鍵會無聲攔截按鍵的 Windows 上停用),而不是要求使用者自行摸索。
- **#833:**完成的專注時段會自動將分鐘數計入所連結的任務——手動輸入是修正途徑,而非工作流程。當某項行為確實無法推斷且需求明確時,應先由一個現有開關控制,再考慮建立新的開關:只有在現有番茄鐘計時器及其任務連結已啟用時,才會顯示任務時間追蹤;沒有新增設定。
**護欄:**加入設定、提示或手動工作流程步驟前,必須證明結果無法推斷或預測。建立新設定或操作入口前,必須證明沒有任何現有設定已表達相同意圖。預設值應遵循平台最佳實務,而非保持中立。可以自動處理時,手動控制只能作為修正的備用方式,絕不能成為主要途徑。
1. 寫入與持久保存
P1——每次寫入都由修訂版號防護;絕不信任抵達順序
必須在最底層(SQL upsert)強制確保較低 rev 的承載資料無法覆寫較高 rev 的資料列,而不是交由呼叫端處理。
- #693(P1,關鍵):
updateTask透過射後不理的saveTask持久保存,但經過防抖的完整快照saveData可能稍後抵達並覆寫較新的資料列——任務 upsert 沒有 rev 防護。在短生命週期的 MCP 程序中,寫入甚至會完全遺失。修正方式:在 upsert 加入WHERE tasks.rev IS NULL OR tasks.rev <= excluded.rev,並在flushPendingSave()中追蹤進行中的儲存。 - **#316:**同步進行期間所做的編輯(完成任務、切換領域),會在進行中的同步重新整理抵達時遭到還原。這歷經三次修正:略過過時的擷取結果、即使編輯發生於同一毫秒也推進本機變更標記,最後加入診斷以找出其餘問題。
**護欄:**任何新的持久保存路徑(新實體、新儲存呼叫、新程序)都必須回答:什麼機制能阻止較舊的承載資料覆寫較新的資料? 如果答案是「時序」,這就是錯誤。
P2——刪除只解除關聯,絕不串聯刪除;還原必須勝過墓碑
- **#609(P0,關鍵):**刪除領域會刪除其中每個任務與專案。更糟的是,使用者從備份還原後,同步的墓碑仍會在每次同步週期重新刪除已還原的資料。修正方式:刪除領域現在只會清除
areaId;備份還原會將復原的記錄標記為新的本機資料,讓過時的遠端墓碑落敗。
**護欄:**刪除容器(領域/專案/區段/標籤)時,應解除子項目的關聯。任何還原/匯入路徑都必須重新標記資料的作者身分(新的 rev/updatedAt),使其不會敗給使用者正試圖從中復原之錯誤所建立的墓碑。
P3——射後不理的成功是假象;只有持久保存完成後才能回覆
- **#694:**MCP 寫入工具從記憶體內儲存區回傳已變更的任務,但
saveData已無聲失敗——讀取操作(使用不同的資料庫連線)與桌面應用程式都顯示舊資料。工具在實際上未保存任何資料的情況下宣告「成功」。 - **#693:**根本原因相同:程序在未追蹤的儲存 Promise 解析前便已結束。
**護欄:**只有儲存層確認後,才能傳送宣告 API/工具/IPC 寫入成功的回應。追蹤每個儲存 Promise;flushPendingSave() 必須涵蓋所有待處理工作。
2. 同步收斂
P4——合併必須是固定點:重新合併已同步的資料時不應產生任何變更
- **#698:**一個已移除的舊版欄位(
showFutureRecurrence)仍存在於其中一端儲存的承載資料。內容簽章透過{...task}展開建立,因此未知鍵值在每次同步都產生 325 個永久衝突,即使 rev 與時間戳記完全相同。 - #418:
null與省略選用欄位的雜湊不同 → 無止境的假衝突,以及由過時實體時間戳記而非時鐘引起的虛假「3 小時時鐘偏移」警告。 - **#142:**舊版無效的
revBy會中止整個合併驗證,直到經過清理為止。
護欄:
- 內容簽章必須根據明確的欄位允許清單計算,絕不展開物件。未知/舊版鍵值絕不能進入比較。
- 正規化必須具備等冪性並能收斂:
normalize(normalize(x)) === normalize(x),且對選用欄位而言null≡ 遺漏。 - 從結構描述移除欄位時,必須提供遷移/移除步驟——舊的承載資料會在遠端存在多年。
- 每次變更簽章時,測試套件中都應包含「對齊資料後再次同步會產生零衝突」的測試。
P5——同步自身的寫入必須對同步觸發器隱形
#718/#725 的「永遠持續同步」事件需要六次修正,每次都是不同的自我回饋迴圈:
- 同步持久保存未標記 SQLite 監看程式的忽略時間範圍 → 自身寫入被視為外部變更;
- 同步狀態/歷史記錄寫入再次將資料狀態標記為髒;
- 狀態寫入儲存了過時的記憶體內快照,撤銷剛合併的實體(再次將同一合併排入佇列);
- 載入時針對重複領域名稱的去重操作在每次載入時重寫資料;
- 人物清單未持久保存至 SQLite → 每次載入時都會回填並標記為髒;
mindwtr.db-shm檔案雜訊被視為外部寫入。
更早的同類錯誤:#309(監看程式把應用程式自己的 data.json 重新合併為外部變更——需要近期自身寫入雜湊的時間範圍,而不能只記住最新一筆)、#502(持續顯示同步活動的介面)。
護欄:
- 同步寫入的每個位元組(資料、狀態、歷史記錄、快照),都必須向每個可能觀察到它的監看程式標記為自身寫入。
- 同步中繼資料(狀態/歷史記錄)必須疊加寫入持久保存的快照,絕不能來自可能過時的介面狀態——更理想的是,將其置於使用者資料之外。
- **載入資料絕不能變更資料。**遷移/去重應是明確的一次性程序,而不是會弄髒狀態的載入時正規化。
- 每個自動觸發器都需要有終止依據:無工作 → 不執行(#725)、失敗 → 冷卻(#718 修正 5、#133 飛航模式)、成功 → 閒置。
P6——同步期間的使用者編輯不可侵犯
- **#323:**點按狀態下拉式選單會觸發取得/失去焦點同步,導致開啟中的編輯器內所有未儲存的編輯遭到還原。修正方式:編輯器開啟時,自動同步會忽略取得/失去焦點觸發器。
- #316 / #128:「每個動作幾秒後都被還原」——這是整段歷史中最破壞信任的症狀。
**護欄:**任何新的同步觸發器都必須考量「如果使用者正在編輯會怎樣?」本機編輯端會重新排入佇列;絕不能遭到覆寫。(這正是 lastDataChangeAt 快照 + LocalSyncAbort + 重新排入佇列機制存在的原因——新的同步工作必須經過這套機制。)
P7——同步文件是一個合併單位;除非有交易方案,否則不要拆分
- **#629(討論):**拆分
archive.json的方案遭到拒絕——封存/取消封存會成為跨檔案交易;檔案後端會獨立上傳檔案 → 產生腦裂。已記錄的方向是記錄層級的累加式同步,而不是增加更多檔案。 - **#113(討論):**Syncthing 的衝突在設計上就是檔案層級;建議的解法是由應用程式控制的後端(WebDAV/雲端),而不是更聰明的檔案命名。
**限制:**除非同時提供不可分割的多檔案提交協定,否則功能不得新增第二份同步文件(或每個實體各自的檔案)。優先在實體上新增欄位,而非新增頂層文件。
3. 附件(二進位資料 + 中繼資料的兩階段規則)
P8——先上傳位元組,再發布中繼資料;404 是終止狀態
- **#176:**同步會清除附件
uri成為"",並在上傳完成之前發布中繼資料 → 產生沒有cloudKey、任何位置都沒有檔案且無法復原的附件。接著,「封鎖直到上傳」的修正又揭露了從未執行的上傳(缺少大小中繼資料的content://URI)→ 同步永遠遭到封鎖。總共歷經四次修正。 - **#213 / #128:**過時的
pendingRemoteDeletes與遺失遠端的中繼資料造成無限重試迴圈;手動刪除資料也無濟於事,因為同步會將其還原。 - **#399:**SAF 結尾斜線文件 URI 讓現有附件目錄不可見 → 每次同步都會重新完整複製所有附件。
- **#655:**遠端 404 無限重試/輪詢;之後又出現 tmp 檔案碰撞造成的
EISDIR。
護欄:
- 順序不可變:上傳位元組 → 記錄
cloudKey→ 發布中繼資料。絕不能反過來。 - 每一種遠端檔案失敗都應分類為可重試(次數有上限並採用退避)或終止(404 → 標記為無法復原並停止)。附件程式碼中絕不能出現無限重試。
- 身分檢查應使用正式 ID/鍵值,而非供應者 URI 字串格式(SAF 結尾斜線、
content://的特殊情況)。 - 中繼資料顯示沒有待處理工作時,應略過附件階段(同時保障效能與迴圈安全)。
- 已知的開放風險(2026-06 審查):
duplicateTask會讓副本共用cloudKey,且沒有參考計數——刪除其中一份會刪除另一份的位元組。不要新增會共用鍵值的路徑。
4. 多程序存取(MCP、CLI、本機 API)
P9——一個正式儲存區、一條序列化寫入路徑;每個寫入者都必須是完整參與者,否則只能讀取
- #179 / #285:CLI 將任務寫入
data.json——該檔案是匯出鏡像,只在啟動時讀取一次,此後便會遭到覆寫——且缺少rev/revBy/taskMode,因此即使成功匯入,合併仍會無聲捨棄這些任務。 - **#722:**桌面應用程式 + 本機 MCP 同時寫入相同 SQLite → 發生鎖定衝突;危險的替代作法(兩個寫入者透過共用儲存空間寫入)必須明確記錄為不受支援。修正方式:遇到寫入者鎖定時立即失敗、重新載入目前資料並重試整個操作——絕不能從鎖定前的快照繼續。
- **#650:**長時間存續的桌面 WAL 讀取快照,在重新啟動前看不到 MCP 寫入。
- **#367:**Mac App Store 沙箱重新配置了資料庫位置;MCP 的路徑解析器只知道非沙箱路徑。
護欄:
- 所有實體建立操作都必須經過核心工廠,為每個同步必需欄位(
rev、revBy、createdAt、updatedAt、預設值)加上標記。手動拼裝 JSON 的寫入者,就是未來的資料遺失錯誤。(雲端伺服器的POST /v1/tasks缺少 rev 標記也屬於同一類——修正仍待完成。) - 外部寫入者必須內嵌核心儲存區 + 儲存配接器,否則只能讀取。
- 跨程序寫入協定:取得鎖定 →(發生衝突時)重新載入 → 重新套用 → 寫入。禁止從過時快照繼續。
- 路徑解析必須列舉沙箱化安裝管道(App Store 容器、Flatpak 等)。
5. 日期、重複規則與狀態語意
P10——共用唯一的「下一個實例」函式;所有欄位一起推進;重新產生必須具備等冪性
重複規則是整段歷史中最常反覆損壞的功能領域:
- **#140:**自訂重複規則推進了
dueDate,卻未推進startTime→ 已完成任務立即重新出現在專注檢視。 - **#241:**重新產生的實例遺失
sectionId/areaId。 - **#187 → #717:**完成後重複的後續任務立即進入下一步行動(沒有延後的
startTime);一個版本週期後,相同症狀又在僅日期的開始日期與模式切換時復發。 - **#662:**完成任務時,即使已有同等的開放後續任務,仍會建立重複項目。
- **#557:**一個小型預覽功能需要 7 次以上的後續修正:每月第 n 個星期幾與每月第幾天、沒有開始日期時的錨點、每月中繼資料在載入/儲存/編輯往返時遺失、預覽標示錯誤。
- **#17:**有 COUNT 上限的 RRULE 從「目前月份」開始展開,讓 10 年前的系列死而復生。
護欄:
- 必須只有一個核心函式計算下一個實例。它必須:一致地推進開始/截止/回顧日期;保留所有情境欄位(專案、區段、領域、情境、標籤、檢查清單重設規則);讓僅日期的值保持為僅日期;以系列開始日期而非「現在」作為 RRULE 展開的錨點;並具備等冪性(若已有同等的開放後續任務則略過)。
- 重複規則中繼資料必須能通過載入 → 儲存 → 編輯往返;每個新的重複規則欄位都要加入往返測試。
- 任何重複規則變更的迴歸矩陣:
{strict, after-completion} × {start-only, due-only, both, neither} × {daily, weekly, monthly-day, monthly-nth-weekday, yearly, COUNT-limited} × {date-only, datetime}。
P11——狀態優先於日期;可見性述詞只能存在於核心的一個位置
- **#341:**因為開始日期規則忽略狀態,WAITING 任務出現在 NEXT 中。
- **#237:**桌面版與行動版以不同方式計算「今天」;任務在一個平台可見,在另一個平台卻不可見。最終修正是讓兩個平台採用同一套共用規則。
- **#144:**排程中→下一步行動的自動提升刻意實作於核心擷取維護中,讓兩個應用程式都能承接。
- #591:儲存庫自身的衍生規則:日期一致性(例如
startTime晚於dueDate)會在核心正規化中集中偵測為衍生狀態,套用至所有寫入路徑——同步輸入則視為不受信任並重新檢查。
護欄:
- 任何決定任務顯示位置的述詞(
isToday、isAvailable、已延後、回顧到期)都應定義於packages/core,並由兩個應用程式匯入。即使目前結果一致,檢視重新實作其中任何一項都是錯誤。 - 狀態是主要軸線;日期只能在狀態內調節,絕不能跨越狀態。
- 不要為了強制一致而自動變更使用者日期——應改為顯示衍生警告(#591 的結論:使用者可能刻意讓任務維持逾期但延後的狀態)。
P12——每個重複任務只有一個作用中實例,是產品不變條件
- #552 → #557:預先建立未來的真實實例遭到刻意否決(「讓完成、刪除、同步與重複項目行為更難推理」);採用的設計是唯讀預覽。
**限制:**需要顯示未來內容的功能應產生衍生/預覽且不可編輯的資料——絕不能提前建立真實記錄。
P13——僅日期的值絕不能隱含時間
- **#298(關鍵):**僅日期的日期被排程為午夜鬧鐘,在夜間喚醒使用者;撤銷通知權限也不會清除已排程的鬧鐘,因此鬧鐘會持續觸發,介面卻沒有任何顯示。
- **#205:**輸入的年份被強制轉換為 19xx;部分日期區段會跳到不同年代。
**護欄:**除非使用者設定時間,否則僅日期的欄位不會排程任何內容。撤銷權限時應取消所有已排程項目。鬧鐘/通知排程必須具備等冪性,且重新註冊時保持安靜(#418 的「已設定此鬧鐘」快顯通知風暴)。
6. 設定與非任務狀態
P14——分類每項設定:同步/裝置本機/介面工作階段——且預設值絕不能在合併中勝出
- **#120:**同步將設定重設為預設值(遠端預設值覆寫本機明確值)。
- **#62:**應用程式升級時遺失設定。
- **#316:**介面領域篩選條件位於會被同步重新整理覆寫的位置。
- **#488:**變更密度會重設文字大小(一項設定的寫入者覆寫其他同層設定)。
**護欄:**新設定在建立時就要宣告其類別。介面工作階段狀態(目前篩選條件、所選檢視)絕不能存在於同步的設定文件中。設定應逐欄位合併;明確值必須永遠勝過預設值。設定寫入必須經過不會捨棄同層鍵值的單一更新器。
7. 發布工程與平台沙箱
P15——每個發行管道都是不同的執行環境;驗證管道成品,而非程式碼庫
- **#715/#674/#723:**v0.9.9 FOSS APK 對每位 F-Droid/Obtainium 使用者都會在啟動時當機(只發生於 FOSS 相依性集合的模組互通失敗)。一般 APK 並無問題——測試一個管道無法證明另一個管道正常。
- **#583(P0):**Flatpak 無法啟動——只有 Flatpak 執行環境缺少 appindicator 共用程式庫。
- #234:AUR 反覆損壞(CI 注入的 tauri 設定、bun
overrides特殊情況,接著是check()中不穩定的測試);最終解法是在乾淨的 Arch 容器中建置 AUR 套件的發布管線閘門——這是管道閘門的典範。 - **#539:**F-Droid JVM 目標修補程式遺漏本機 Android 模組。
- **#209:**Windows Store 當機:系統匣/全域快捷鍵初始化在 MSIX 沙箱中造成致命錯誤;已註冊快捷鍵的衝突也可能終止啟動。
- **#198:**Hermes 當機——在計時器全域變數存在前,於模組範圍參照
setTimeout。
護欄:
- 啟動絕不能硬性依賴選用能力(系統匣、全域快捷鍵、鑰匙圈、通知、模組範圍計時器)。偵測 → 降級 → 記錄;絕不能結束。
- 每個管道(FOSS APK、Play、F-Droid、Flatpak、AUR、MSIX/Store、winget、homebrew、App Store、TestFlight)都要在發布前,於 CI 所允許且盡量忠於管道的環境中,冒煙啟動該管道自身的成品。
- 管道特定的相依性集合(FOSS 與非 FOSS、Flatpak 鎖定檔)應視為各自獨立的建置目標,並有各自的 CI。
P16——已加標籤的版本不可變
- **#682/#674:**修正 FOSS 當機後,從較晚提交建置的修補版 APK 被上傳至現有 v0.9.9 版本。IzzyOnDroid 的可重現建置檢查失敗;該成品必須撤下,並將此版本標記為已遭取代。
**護欄:**修正 → 新標籤 → 新版本(例如 v0.9.9.1),絕不能取代成品。將建置步驟保留在納入版本控制的儲存庫指令碼中(而非工作流程 YAML 內嵌內容),讓下游可重現建置工具能自動追蹤變更。
P17——沙箱會拒絕你假設存在的事物;絕不要持久保存尚未成功過的設定
- **#335:**Flatpak 沒有鑰匙圈/DBus → 安全儲存失敗;修正加入本機密碼備援。同一份報告中,Tauri 外掛 ACL/鎖定檔漂移也使 HTTP 損壞。
- **#343:**iOS 資料夾權限在重新啟動時消失 → 需要安全範圍書籤。
- **#617:**Homebrew 建置缺少 CloudKit 權利;發布工作流程現在會驗證已簽署成品中的權利。
- **#338:**CloudKit 結構描述部署至 Development 而非 Production——所有 App Store 使用者都無法使用。此外,選定的後端在第一次成功同步之前就持久保存至設定,造成當機迴圈。修正方式:暫存後端,直到第一次成功。
- **#727:**新增人物實體時遺漏 CloudKit 可查詢索引 → 重設 iCloud 資料後同步損壞。
護欄:
- 每個平台/管道都要有能力矩陣:鑰匙圈、檔案系統持久保存、網路傳輸、推送、CloudKit——每項能力都要有偵測與備援。
- 同步後端設定只有在成功往返一次後才能提交。
- 伺服器端結構描述(CloudKit 記錄型別、索引、正式環境部署;雲端伺服器驗證)是實體檢查清單與發布檢查清單的一部分。
P18——「新增實體型別」是一份檢查清單,而非一個型別定義
此原則源自 #727(人物缺少 CloudKit 索引)、#718-修正-6(人物未持久保存至桌面 SQLite → 永久髒迴圈)、#322(SQLite 儲存順序違反 FK 順序,導致 FOSS Android 重新啟動時清除資料),以及雲端 POST /v1/tasks 的 rev 標記缺口。
新實體(或新欄位)必須涵蓋:核心型別 + 工廠標記;正規化/簽章允許清單;SQLite 結構描述與符合 FK 安全的儲存順序;data.json 匯出;同步合併 + 墓碑;CloudKit 記錄型別 + 索引 + 正式環境部署;雲端伺服器驗證/標記;兩個應用程式的儲存區;i18n 標籤;往返與合併收斂測試。
8. 大規模資料下的效能
P19——互動成本為 O(可見項目),而非 O(儲存區);由合成的大型儲存區預算強制執行
- **#594(討論)+ #643–#649、#195、#224:**一般大小的使用者資料庫(186 kB JSON!)讓每次點按都凍結長達一秒。找出的原因包括:每個專案資料列都掃描完整任務清單;開啟編輯器時反覆重新掃描所有任務;每次變更都完整保存快照;廣泛的儲存區訂閱讓整個畫面因無關變更而喚醒;每個附件都重新列出 SAF 目錄;沒有待處理工作仍進入附件同步階段;不具等冪性的點按處理常式不斷堆疊(排隊點按產生 15 個空白檢查清單項目)。
護欄:
- 新的清單檢視必須使用虛擬化與窄範圍選取器;畫面中不得出現完整陣列的
useTaskStore()訂閱。 - 變更的持久保存應採用累加式;完整快照儲存只能用於生命週期/同步邊界。
- 每個定期/同步階段都要在無工作時提早結束。
- 點按處理常式應具備等冪性(排入佇列的重複點按不會造成影響)。
- 每次新增高頻路徑時,大型儲存區效能套件(
bun run test:perf,預算位於docs/performance-budgets.md)都要加入對應案例。
9. 介面、i18n 與一致性
P20——不得硬編碼面向使用者的字串;功能加入時,鍵值必須同步加入所有語系
有十一個不同的問題(#244、#245、#246、#256、#257、#261、#287、#292、#215、#23、#593)屬於「硬編碼英文文字」或「缺少鍵值」——每一個都能在審查時輕易預防。日期/星期格式化應透過語系感知 API(#375、#287)。
P21——桌面版/行動版一致性是功能的一部分,而非後續工作
- #237(同一任務在行動版可見、桌面版不可見)、#99、#149、#559、#314:一致性缺口一次又一次地以錯誤形式浮現。
**護欄:**任何任務語意/可見性的變更都必須落在核心,並由兩個應用程式在同一版本採用;否則必須明確記錄差異是刻意設計(例如 #725 刻意讓行動版保留生命週期同步的決策)。
10. 復原、診斷與修正工作流程
P22——復原必須能擊敗它要處理的失敗
- #236:「最多 5 份快照」全都來自相同的 4 分鐘——對任何真實事件都毫無用處。修正方式:在整個時間範圍分散保留 + 資料未變更時略過快照。
- **#609:**還原的備份會被摧毀資料的同一個同步錯誤重新刪除,直到還原程序學會重新標記記錄的作者身分。
**護欄:**必須針對實際的遺失情境(串聯刪除 + 墓碑、損壞的遠端、錯誤合併)測試還原路徑,而不只是檢查「檔案存在」。
P23——修補看似合理的原因前,先讓不可見的決策可供診斷
- #698 能在一次處理中解決,正是因為診斷會公開每個衝突的
diffKeys。 - #718 在加入診斷揭示真正的兩個原因(人物回填 +
-shm雜訊)前,浪費了三次推測性修正。 - #316 的最後一項動作是加入合併解析診斷(勝出端、revs、時間戳記),而不是再次猜測。
**護欄:**合併/衝突路徑要記錄原因 + 範例欄位鍵值(經過遮蔽);迴圈類錯誤要先加入觸發來源診斷,再進行修正。對於語意錯誤(重複規則、日期邏輯),修正前應以測試列舉輸入矩陣——#187/#717 與 #557 的事件,都是在多維輸入空間中反覆進行單一案例修補。
P24——重現-確認迴圈
這段歷史中經證明有效且應予保留的做法:使用 status:needs-confirmation 搭配嚴重程度/優先順序標籤;貼出含完整提交雜湊的修正;要求回報者提供結構化環境資訊 + 記錄(安裝管道很重要——#322 只發生於 FOSS 建置、#617 只發生於 homebrew);迅速撤回不良成品,並坦率說明(#674/#682)。
新功能快速檢查清單
實作前,請回答:
- **寫入者:**還有誰會寫入此資料(同步、MCP、CLI、其他裝置、舊版應用程式)?什麼機制防護順序?(P1、P9)
- **收斂:**我的變更同步一次後,第二次同步是否產生零差異——包括與執行舊版本的裝置同步?(P4)
- **觸發器:**我的程式碼是否會寫入監看程式/觸發器可見的內容?是否標記為自身寫入?每個迴圈都會終止嗎?(P5)
- **編輯中:**如果使用者在執行期間編輯,會發生什麼事?(P6)
- **實體/欄位:**我是否逐項檢查實體清單(SQLite + FK 順序、簽章、CloudKit + 索引 + 正式環境部署、雲端驗證、工廠)?(P18)
- **日期:**狀態優先?僅日期維持僅日期?兩個應用程式共用核心述詞?涵蓋重複規則矩陣?(P10–P13)
- **設定:**屬於同步、裝置本機或工作階段?預設值是否可能勝過明確值?(P14)
- **管道:**這是否會影響啟動、原生能力或相依性?哪些管道需要冒煙測試?(P15–P17)
- **規模:**處理 5 千個任務時成本為何?無工作時是否提早結束?(P19)
- **失敗:**每次重試是否有上限、每個 404 是否為終止狀態、每次還原是否重新標記作者身分?(P8、P22)