這是 Weekend Challenge: Passion Edition 的投稿

What I Built

目標是將現有的 Claude 技能轉換為 Google AI。Gemini 已不再適用於免費方案,因此這是 Google Antigravity。

每個 AI 程式碼代理都有相同的失敗模式,無論背後是哪個模型:它會以絕對的信心陳述關於你程式碼庫的內容,但卻是錯的,因為信心與驗證是兩回事,而在典型的對話中,沒有任何機制能強制執行第二件事。它假設備用路徑是安全的,因為它看起來應該是安全的。它把對共享類型的變更稱為「additive」,因為 diff 看起來是 additive,而沒有檢查實際上誰導入了它。它把測試套件通過當作行為得以保留的證明,而實際上測試套件通過只證明行為對某人已經想到寫下的輸入得以保留。這不是模型不夠聰明的問題。隨著對話變長,情況會變得更糟,而不是更好,因為上下文會充滿只是因為十分鐘前被說過而感覺為真的東西。

Session Discipline Kit 之所以存在,是因為「雙重檢查你的工作」是一條代理可能點頭同意、然後悄悄不遵守的指令,就像一個人在期限壓力下也可能這樣做一樣。散文式的規則會被不一致地遵守。機制則不會。因此,與其再多一段放在上下文檔案中要求代理在編輯前驗證的文字,這個工具包把規則直接寫入環境本身:一個鉤子,會阻擋任何代理在當前對話中尚未實際開啟並讀取過的檔案的編輯——完全阻擋,無論它聽起來多麼有信心。這個機制能捕捉到整個類別的錯誤在送出前發生,而不是三週後以「奇怪、罕見、難以重現」的行為浮現,那時追蹤問題所花的時間會是原本缺少讀取動作的四倍。

驗證是底線,而不是整個系統。其餘是九項技能,對應真實變更的實際形狀,按照實際發生的順序:先陳述足夠具體、可被否證的範圍,再開始寫任何程式碼,這樣之後才有具體的東西可以拿來檢查 diff;接觸共享檔案前,先列出它的每一個真實消費者,因為在 monorepo 中 diff 總是看起來很小,而爆炸半徑才是真正重要的數字;為任何「正確與否取決於輸出形狀而非單一布林值」的情境搭建 golden-snapshot 測試——這正是測試套件通過卻送出真正回歸的地方,因為損壞的輸入從未進入任何人手動挑選的集合;以陌生人的視角審查 diff,沒有撰寫它的記憶,也不因當時感覺合理的推理而給予信用;以及在對話結束時,從實際的目前 repo 狀態重新產生交接文件,而不是從對話對自己的記憶產生。

最後這一點值得停下來思考,因為這正是其餘工具包最終要保護的問題。新的 AI 對話從零開始,沒有上一個對話的記憶,而這種情況的一切都推使代理要不就重新推導它已經擁有的上下文——緩慢——要不就悄悄假設它實際上並未重新檢查過的東西——快速,最終錯誤。從 git status 和真實 diff 建立的交接文件,而不是從對話對自己的記憶建立,能從根本打破這個循環:下一個對話繼承的是已驗證的狀態,而不是別人未經驗證的信心,無論其初衷多麼良好。

這一切都不是對「什麼傾向出錯」的猜測。它是從六種真實、已歸納的失敗模式建構而成,階段化為十二點方法論,精確命名每個規則保護的是變更的哪一個階段,另外還有一篇專門深入探討它最嚴重咬人的地方:TypeScript monorepo 中的集中式配置,在那裡一個小而自信但錯誤的編輯,會在任何人注意到之前就影響到每一個下游消費者。安裝它,把兩個鉤子連接到 Antigravity 的鉤子系統,並在你的上下文檔案中指向一行。紀律不再是你每一次對話都必須記得要求的事,而開始成為環境自然強制執行的東西。

Demo

安裝

python3 install.py /path/to/repo

Code

Session Discipline Kit for Antigravity CLI

阻止 Antigravity 自信地猜測——阻擋未經驗證編輯的鉤子,以及能清楚界定工作範圍並在對話間乾淨交接的技能。

The problem

一個 AI 程式碼代理開始新對話時,沒有上一個對話的記憶、沒有內建對共享檔案變更影響範圍的感知,也沒有機制強制它在對程式碼庫做出主張前實際讀取檔案。任其發展,會產生一組特定、重複發生的失敗:備用路徑默默吸收它們從未被設計處理的情況;對集中式檔案看起來是 additive 的變更,卻破壞了其他四個套件;測試套件通過卻送出真正的回歸,因為損壞的輸入剛好不在任何人想到寫下的集合中;對話從一行 bug 修復逐漸漂移成無法審查的六個檔案變更,卻沒有陳述可供檢查的範圍。

None of this is…

How I Built It

那個時間軸就是它的形狀——這裡是它的質地。

這個工具包一開始並不是開源專案。它一開始是一個開發者日常工作的私人紀律系統:一位獨立創辦人正在建立一個大量使用編譯器的 TypeScript 平台,厭倦看著 AI 程式碼代理自信地陳述關於它實際上並未檢查過的程式碼庫的內容。它安靜地存在於 .claude/ 資料夾中,履行它的職責,直到它所建立的基礎發生變化——Google 端的一次平台轉型切斷了下一個工具所依賴的免費方案,而原本對一個工具有效的設定,突然需要對完全不同的工具生效。

移植過程正是它不再是例行重寫的地方。第一個目標 Gemini CLI 與原始版本有許多共通之處——同樣的 JSON-over-stdin 鉤子合約、同樣的事件驅動形狀——直到它們不一樣為止:鉤子設定自信地寫入,而 CLI 自己的啟動輸出立即標記某個事件名稱為無效。這不是透過更多研究更正的猜測,而是真實的工具拒絕真實的設定,並清楚說明原因。修正之後,第二個更細微的不匹配以同樣的方式出現——工具的內部名稱與顯示名稱不符,這是平台團隊自己曾回報過的命名怪癖。然後,在那個版本甚至還沒完全穩定之前,平台本身就被免費方案帳戶停用,並被替換成全新的東西:一個獨立、閉源的重寫版本,其鉤子系統文件極少,不同來源的描述不一致,有時甚至互相矛盾。這一次沒有乾淨的主要來源可以查證,因此回應方式是針對歧異來建構,而不是猜測過去——在每一個可能的命名慣例下註冊鉤子,讓驗證邏輯盡可能關注檔案狀態而非工具特定名稱,並在程式碼中清楚說明哪些部分已確認、哪些是合理的推測。

這正是值得停下來思考的部分:一個目的完全在於拒絕相信未經驗證主張的工具包,是透過在每一步都對自己套用完全相同的標準來建構的。目標平台鉤子結構的任何部分,都不會因為部落格文章自信地這麼說而被接受——它會根據實際運行的工具做了什麼來檢查,這和這個工具包堅持代理必須檢查檔案而不是相信自己對檔案的記憶是一樣的。當那個檢查第一次失敗時,它會以對話偏離軌道時被修正的相同方式被修正:不是試著更努力猜對,而是查看真實輸出並更新。這個方法論不只是被建立然後另外被證明有效。建立它的過程,就是它第一次真正的測試。

Prize Categories

Best Use of Google AI