規格來源:2026-08-16 的來源受理面 grill 場次。缺口與決定記錄在 #96,本規格是它的實作規格。
Problem Statement
串接一組供應商 API 時,總有一部分規範性資訊只存在於供應商的信件或通訊軟體訊息裡——金鑰怎麼取得、測試環境的位址、上線前必須完成的事、某個商戶參數的實際意義。它不在任何一份正式文件裡,而供應商往往也沒有打算把它寫進去。
對操作者而言,現在只有兩條路,兩條都不好。第一條是不把它放進 pipeline:資訊變成 missing,產出的整合契約缺一塊,但至少報告是誠實的。第二條是讓 agent 讀到那段內容並寫進擷取結果:契約完整了,但 provenance.json 會宣稱這條主張有來源支撐,而那個來源不存在於任何 manifest 條目裡——報告在說謊,且沒有任何機制會發現。
同一個問題也出現在供應商另外給的欄位對照試算表上:內容是真的,出處是真的,但載體不是一份可以重新取得、可以比對雜湊、可以被 check-freshness 週期性重走的文件。
操作者需要的是第三條路:讓這些內容進得來、留得下出處、但不冒充正式文件。
Solution
新增一個「次級佐證」(supplementary)的來源等級,與現有的正式文件(normative)並列。
操作者把信件內容人工摘錄成 Markdown,用一個新的匯入指令放進 sources/,指令同時寫出一份記載出處的 sidecar:寄件者、日期、主旨、摘錄者。manifest 掃描時讀到 sidecar,就把該來源標成 supplementary。
次級佐證能做的事:被引用、填補 missing、讓一條主張成立。不能做的事:與正式文件衝突時勝出、在報告裡與正式文件混為一談。凡是只靠次級佐證才成立的主張,都會在驗證報告裡被個別點名——不是一則籠統的「本次 run 用到了補充資料」警告,而是逐條指出「這條規範性主張的唯一依據是一封信」。
操作者由此得到一個誠實的中間狀態:契約是完整的,而契約裡每一塊由誰背書、背書強度如何,讀報告的人一眼看得到。
User Stories
- 身為串接工程師,我想把供應商信件裡的金鑰取得流程摘錄成文件放進來源目錄,這樣那段資訊就能進入整合契約而不必假裝它來自技術手冊。
- 身為串接工程師,我想在摘錄的同時記下寄件者與日期,這樣三個月後有人問「這句話哪來的」時我答得出來。
- 身為串接工程師,我想在摘錄裡記下摘錄者是誰,這樣摘錄若寫錯了,責任歸屬是清楚的。
- 身為串接工程師,我想讓摘錄檔的匯入在時間戳沒帶時區時就失敗,這樣不會留下一個無法排序的出處紀錄。
- 身為串接工程師,我想讓匯入指令拒絕覆蓋既有檔案,這樣不會靜默蓋掉先前的證據。
- 身為串接工程師,我想讓匯入指令拒絕不安全的檔名與路徑,這樣來源目錄不會被寫到預期之外的位置。
- 身為串接工程師,我想讓供應商給的欄位對照試算表在另存為 Markdown 表格後也走同一條次級佐證路徑,這樣兩種補充載體不需要兩套機制。
- 身為串接工程師,我想在 manifest 裡直接看到哪些來源是次級佐證,這樣不必回頭去翻 sidecar 才知道這次 run 用了什麼。
- 身為串接工程師,我想讓沒有 sidecar 的來源自動被視為正式文件,這樣既有的所有來源與 run 不需要任何改動。
- 身為串接工程師,我想讓次級佐證支撐的主張在驗證報告裡被逐條點名,這樣我知道契約的哪幾塊站在比較薄的地基上。
- 身為串接工程師,我想讓這個點名精確到單一 plan item 而不是整個 run,這樣它不會像既有的來源事實未掃描警告那樣變成背景噪音。
- 身為串接工程師,我想讓次級佐證無法推翻正式文件的說法,這樣一封記錯的信不會覆蓋掉手冊上寫對的內容。
- 身為串接工程師,我想讓同時有正式文件與次級佐證支撐的主張不被特別標記,這樣標記只出現在真正需要注意的地方。
- 身為串接工程師,我想在加入一份信件摘錄之後,原本靠單一文件歸屬的引用仍然歸屬得到,這樣補充資料不會反過來讓整份手冊失去歸屬。
- 身為審查者,我想在離線審查頁上分辨哪些內容來自次級佐證,這樣我在核准前知道該多看哪幾條。
- 身為審查者,我想讓次級佐證的出處資訊出現在 provenance 裡,這樣追溯不必離開產出物。
- 身為維運者,我想讓
check-freshness 不對次級佐證來源給出無意義的判定,這樣重新取得性檢查的結果仍然可信。
- 身為維運者,我想讓治理掃描不因為一份無法重新取得的來源而卡住,這樣既有的排程流程不受影響。
- 身為維運者,我想讓次級佐證來源在來源風險檢查裡照常被掃描,這樣一段從信裡貼進來的文字不會繞過注入防護。
- 身為維運者,我想讓摘錄檔裡不慎貼進來的金鑰在進入擷取之前就被擋下,這樣補充載體不會變成外洩管道。
- 身為專案負責人,我想讓次級佐證的比例反映在文件品質分數裡,這樣我看得出一份契約有多少是靠信件撐起來的。
- 身為專案負責人,我想在版本比較裡看到某條主張的支撐等級改變了,這樣供應商後來把某件事寫進正式文件時我會知道。
- 身為新進工程師,我想從既有的匯入指令類推出這個新指令的用法,這樣我不必學一套新的心智模型。
- 身為 agent,我想從 manifest 就知道哪些來源是次級佐證,這樣我在擷取時知道哪些內容不該當成正式文件引用。
- 身為 agent,我想讓次級佐證與正式文件衝突時有明確的解決規則,這樣我不必自己判斷該信哪一邊。
- 身為稽核者,我想讓每一份次級佐證的出處紀錄不可竄改地綁定該檔的雜湊,這樣出處與內容不會各說各話。
Implementation Decisions
唯一的 schema 變更是來源層的等級欄位。 manifest 的本機來源模型新增一個可選的 authority 欄位,取值 normative 與 supplementary,預設 normative。這個預設在語意上是正確的而不只是為了相容:現存的每一份來源都是正式文件。
下游不新增任何欄位。 plan 的每個 item 都帶著引用,引用帶著 manifest 來源身份;provenance 條目同樣帶著 manifest 來源身份。因此「這條主張只靠次級佐證成立」是推導得出來的,不需要在 plan、provenance 或驗證報告的模型上加欄位。這是刻意的:每多一個要各自維護的欄位,就多一處會與 manifest 說法不一致的地方。
等級由 sidecar 標定,不由 CLI 選項或目錄約定。 掃描器讀到來源同名的 sidecar 就採用其中的 authority,讀不到就是 normative。CLI 選項是一次性宣告,不留在來源目錄裡,換一台機器就消失;目錄約定則讓等級隨檔案位置改變,而相對路徑同時是引用的識別鍵,搬動的代價比看起來大。等級與出處是同一件事的兩面——一份東西之所以是次級,正是因為它的出處是一封信——寫在同一個檔案裡就不會出現兩者各說各話的情況。
新指令沿用既有的瀏覽器渲染匯入形狀。 那條路徑要解的問題完全相同:一份沒有可驗證出處的檔案,靠強制記錄出處欄位、帶時區的時間戳、檔案雜湊、版本化的 sidecar schema 與 fail-closed 的讀取端驗證,換得可追溯性。新指令要求的欄位是寄件者、日期、主旨、摘錄者,取代原本的 URL 與擷取方式。sidecar 帶 schema_version,讀取端對未知版本 fail closed。
單一文件歸屬必須排除次級佐證。 plan 的分類邏輯有一條規則:當 manifest 收斂成剛好一份可用文件時,沒有定位資訊的引用仍可歸屬給它。若次級佐證計入文件數,一個原本只有一份手冊的 run 在加入一份信件摘錄後會變成兩份文件,該規則失效,原本成立的主張會集體掉成未驗證——補充資料反而讓正式文件失去歸屬。因此文件計數只計 normative 來源。這條是本規格裡最容易被忽略、後果最大的一項。
衝突解決規則是正式文件勝。 當同一條主張同時有正式文件與次級佐證的引用且兩者說法不同,結果是正式文件的說法,並且不產生來源衝突 issue——衝突 issue 的語意是「兩個同級權威互相矛盾,人得去判斷」,而這裡的判斷規則是確定的。反之,兩份次級佐證彼此矛盾仍然是來源衝突。
個別標記的呈現形式是驗證報告裡逐條的警告級 issue,指向具體的 plan item 位置。不採 run 層級的單一警告:既有的來源事實未掃描警告已經證明 run 層警告會噪音化——九個 benchmark case 永久帶著它,以致於需要一整段文件來解釋該警告的三種成因如何分辨。「這條規範性主張的唯一依據是一封信」不該落到同一個下場。
plan item 的狀態維持 supported。 不改用未驗證:未驗證的觸發條件是引用對不上 manifest 來源,而摘錄對得上——它有檔案、有雜湊、有 sidecar。混用會讓既有的未驗證修正指引自相矛盾,該指引建議「重讀受影響範圍並補上引用」,但摘錄沒有東西可以重讀。也不新增第五個 plan item 狀態值:該列舉被驗證、評分、版本比較、provenance 共同消費,為一個正交的維度加值會讓每個消費端都得處理組合爆炸。等級屬於來源,狀態屬於主張,兩者不合併。
次級佐證照常通過既有的所有前置閘。 摘錄是 Markdown,來源風險檢查照掃(一段從信裡貼進來的文字同樣可能帶注入內容),來源品質評估照跑,來源事實掃描照掃。等級只影響歸屬與標記,不影響任何既有檢查是否執行。
重新取得性檢查應排除次級佐證來源。 指紋記錄與新鮮度比對的前提是來源可被重新取得並比對雜湊,次級佐證從定義上不可重新取得。把它們納入只會產生無意義的判定並污染批次掃描的結論。
接受的破口,必須寫進 ADR。 摘錄是人寫的,摘錄者可能寫錯或過度解讀,pipeline 無法分辨。sidecar 記下摘錄者是為了可追責,不是可驗證。這是這條路徑與其他所有來源的本質差異,也是整個次級佐證設計唯一真正的代價。ADR 要寫明這一點,並附可證偽條件。
文件同步是非商榷的。 本功能落地必須在同一個 commit 更新代理指引的套件邊界表與 README 的來源格式段落。
Testing Decisions
好的測試在這個 repo 有明確定義:測公開行為,不測私有協作者;期望值獨立導出,不由重跑生產邏輯得到;抗重構。本規格遵循既有的 Red → Green → Verify 循環,每一個可觀察行為各自一輪。
Seam 1|新匯入指令。觀察點是退出碼與寫出的檔案內容——摘錄檔的位元組、sidecar 的欄位、以及拒絕情境下什麼都沒寫。既有對照是瀏覽器渲染匯入的測試,形狀完全一致:成功路徑驗證三份輸出的內容,失敗路徑逐條驗證拒絕原因(輸出已存在、檔名不是單一檔名、時間戳沒帶時區、輸出位置重疊)。新指令的拒絕清單照抄該模式。
Seam 2|assemble 的 run-dir 產物。觀察點是產出的檔案:run-dir 裡的 manifest 呈現正確的等級、plan 裡只靠次級佐證成立的 item 被標記、驗證報告出現對應的逐條警告、同時有正式文件支撐的 item 不被標記。不對中間函式立 seam——manifest 由 assemble 重建,等級在 run-dir 產物裡就看得到,多一個 seam 只是多一處耦合要維護。
必要的回歸測試:一份正式文件加一份次級佐證的 run,其原本靠單一文件歸屬的主張必須維持 supported。這是單一文件歸屬規則那條決定的守門測試,也是本功能最可能造成既有 run 破壞的地方,必須是永久保留的回歸測試。
衝突解決的正反例:正式文件與次級佐證矛盾時取正式文件且不產生衝突 issue;兩份次級佐證矛盾時仍產生衝突 issue。
既有 benchmark 的前後對照:十三案在本功能落地前後的驗證結果必須完全一致——沒有任何案例使用次級佐證,所以任何差異都是回歸。這比新增測試更能證明預設值的相容性。
Out of Scope
原始 .eml 檔的解析與匯入。收件標頭、副本名單、簽名檔與附件會把個資直接倒進來源目錄,這條路徑在 grill 中被明確否決。
Excel 檔的自動轉檔。試算表一律由操作者用外部工具另存為 CSV 或 Markdown 表格後再走本規格的路徑;不建轉檔器的理由與副檔名表的處理見 #98 與 #100。
PII 與密鑰偵測前移到來源風險檢查。那是本功能的前置條件而非本規格的一部分,獨立追蹤於 #101。次級佐證載體放行之前該閘必須先就位,兩者的合併時序需要協調。
端點宣告寫法的辨識放寬(#97)、.docx 與 GitBook 路徑的未驗證標註(#99)。皆為同一場 grill 的其他缺口,彼此無共同設計問題,不併入本規格。
次級佐證在離線審查頁與文件品質分數裡的呈現細節。使用者故事已涵蓋需求,但具體的版面與權重留給實作時依既有慣例決定,不在本規格預先綁定。
供應商後來把某件事寫進正式文件時的自動升級。目前的預期做法是操作者移除摘錄並重跑,自動偵測升級不在本次範圍。
Further Notes
本規格的核心不變式沒有改變:供應商來源仍是規範性主張的唯一權威,來源沒說的仍然留空並記進 missing。改變的是「來源」這個詞涵蓋的載體多了一種,而那一種帶著明確的、可見的、無法被誤認的較低背書強度。
這個設計刻意不模仿領域層既有的宣告支撐/推導支撐/矛盾/不足那組關係。那組關係綁在主張與片段之間,描述的是推理距離;載體可信度是來源固有的屬性,對它承載的每一條主張都成立。把後者塞進前者會讓之後沒有人分得出一個推導支撐是哪一種。
EffectiveValueAuthority 是一個近似前例——它證明這個 repo 已經接受過「同一個值可以有不同權威來源」的模型——但那條軸是文件對實測,與本規格的正式對補充是不同的軸,兩者不應合併。
規格來源:2026-08-16 的來源受理面 grill 場次。缺口與決定記錄在 #96,本規格是它的實作規格。
Problem Statement
串接一組供應商 API 時,總有一部分規範性資訊只存在於供應商的信件或通訊軟體訊息裡——金鑰怎麼取得、測試環境的位址、上線前必須完成的事、某個商戶參數的實際意義。它不在任何一份正式文件裡,而供應商往往也沒有打算把它寫進去。
對操作者而言,現在只有兩條路,兩條都不好。第一條是不把它放進 pipeline:資訊變成
missing,產出的整合契約缺一塊,但至少報告是誠實的。第二條是讓 agent 讀到那段內容並寫進擷取結果:契約完整了,但provenance.json會宣稱這條主張有來源支撐,而那個來源不存在於任何 manifest 條目裡——報告在說謊,且沒有任何機制會發現。同一個問題也出現在供應商另外給的欄位對照試算表上:內容是真的,出處是真的,但載體不是一份可以重新取得、可以比對雜湊、可以被
check-freshness週期性重走的文件。操作者需要的是第三條路:讓這些內容進得來、留得下出處、但不冒充正式文件。
Solution
新增一個「次級佐證」(supplementary)的來源等級,與現有的正式文件(normative)並列。
操作者把信件內容人工摘錄成 Markdown,用一個新的匯入指令放進
sources/,指令同時寫出一份記載出處的 sidecar:寄件者、日期、主旨、摘錄者。manifest 掃描時讀到 sidecar,就把該來源標成 supplementary。次級佐證能做的事:被引用、填補
missing、讓一條主張成立。不能做的事:與正式文件衝突時勝出、在報告裡與正式文件混為一談。凡是只靠次級佐證才成立的主張,都會在驗證報告裡被個別點名——不是一則籠統的「本次 run 用到了補充資料」警告,而是逐條指出「這條規範性主張的唯一依據是一封信」。操作者由此得到一個誠實的中間狀態:契約是完整的,而契約裡每一塊由誰背書、背書強度如何,讀報告的人一眼看得到。
User Stories
check-freshness不對次級佐證來源給出無意義的判定,這樣重新取得性檢查的結果仍然可信。Implementation Decisions
唯一的 schema 變更是來源層的等級欄位。 manifest 的本機來源模型新增一個可選的
authority欄位,取值normative與supplementary,預設normative。這個預設在語意上是正確的而不只是為了相容:現存的每一份來源都是正式文件。下游不新增任何欄位。 plan 的每個 item 都帶著引用,引用帶著 manifest 來源身份;provenance 條目同樣帶著 manifest 來源身份。因此「這條主張只靠次級佐證成立」是推導得出來的,不需要在 plan、provenance 或驗證報告的模型上加欄位。這是刻意的:每多一個要各自維護的欄位,就多一處會與 manifest 說法不一致的地方。
等級由 sidecar 標定,不由 CLI 選項或目錄約定。 掃描器讀到來源同名的 sidecar 就採用其中的
authority,讀不到就是normative。CLI 選項是一次性宣告,不留在來源目錄裡,換一台機器就消失;目錄約定則讓等級隨檔案位置改變,而相對路徑同時是引用的識別鍵,搬動的代價比看起來大。等級與出處是同一件事的兩面——一份東西之所以是次級,正是因為它的出處是一封信——寫在同一個檔案裡就不會出現兩者各說各話的情況。新指令沿用既有的瀏覽器渲染匯入形狀。 那條路徑要解的問題完全相同:一份沒有可驗證出處的檔案,靠強制記錄出處欄位、帶時區的時間戳、檔案雜湊、版本化的 sidecar schema 與 fail-closed 的讀取端驗證,換得可追溯性。新指令要求的欄位是寄件者、日期、主旨、摘錄者,取代原本的 URL 與擷取方式。sidecar 帶
schema_version,讀取端對未知版本 fail closed。單一文件歸屬必須排除次級佐證。 plan 的分類邏輯有一條規則:當 manifest 收斂成剛好一份可用文件時,沒有定位資訊的引用仍可歸屬給它。若次級佐證計入文件數,一個原本只有一份手冊的 run 在加入一份信件摘錄後會變成兩份文件,該規則失效,原本成立的主張會集體掉成未驗證——補充資料反而讓正式文件失去歸屬。因此文件計數只計 normative 來源。這條是本規格裡最容易被忽略、後果最大的一項。
衝突解決規則是正式文件勝。 當同一條主張同時有正式文件與次級佐證的引用且兩者說法不同,結果是正式文件的說法,並且不產生來源衝突 issue——衝突 issue 的語意是「兩個同級權威互相矛盾,人得去判斷」,而這裡的判斷規則是確定的。反之,兩份次級佐證彼此矛盾仍然是來源衝突。
個別標記的呈現形式是驗證報告裡逐條的警告級 issue,指向具體的 plan item 位置。不採 run 層級的單一警告:既有的來源事實未掃描警告已經證明 run 層警告會噪音化——九個 benchmark case 永久帶著它,以致於需要一整段文件來解釋該警告的三種成因如何分辨。「這條規範性主張的唯一依據是一封信」不該落到同一個下場。
plan item 的狀態維持 supported。 不改用未驗證:未驗證的觸發條件是引用對不上 manifest 來源,而摘錄對得上——它有檔案、有雜湊、有 sidecar。混用會讓既有的未驗證修正指引自相矛盾,該指引建議「重讀受影響範圍並補上引用」,但摘錄沒有東西可以重讀。也不新增第五個 plan item 狀態值:該列舉被驗證、評分、版本比較、provenance 共同消費,為一個正交的維度加值會讓每個消費端都得處理組合爆炸。等級屬於來源,狀態屬於主張,兩者不合併。
次級佐證照常通過既有的所有前置閘。 摘錄是 Markdown,來源風險檢查照掃(一段從信裡貼進來的文字同樣可能帶注入內容),來源品質評估照跑,來源事實掃描照掃。等級只影響歸屬與標記,不影響任何既有檢查是否執行。
重新取得性檢查應排除次級佐證來源。 指紋記錄與新鮮度比對的前提是來源可被重新取得並比對雜湊,次級佐證從定義上不可重新取得。把它們納入只會產生無意義的判定並污染批次掃描的結論。
接受的破口,必須寫進 ADR。 摘錄是人寫的,摘錄者可能寫錯或過度解讀,pipeline 無法分辨。sidecar 記下摘錄者是為了可追責,不是可驗證。這是這條路徑與其他所有來源的本質差異,也是整個次級佐證設計唯一真正的代價。ADR 要寫明這一點,並附可證偽條件。
文件同步是非商榷的。 本功能落地必須在同一個 commit 更新代理指引的套件邊界表與 README 的來源格式段落。
Testing Decisions
好的測試在這個 repo 有明確定義:測公開行為,不測私有協作者;期望值獨立導出,不由重跑生產邏輯得到;抗重構。本規格遵循既有的 Red → Green → Verify 循環,每一個可觀察行為各自一輪。
Seam 1|新匯入指令。觀察點是退出碼與寫出的檔案內容——摘錄檔的位元組、sidecar 的欄位、以及拒絕情境下什麼都沒寫。既有對照是瀏覽器渲染匯入的測試,形狀完全一致:成功路徑驗證三份輸出的內容,失敗路徑逐條驗證拒絕原因(輸出已存在、檔名不是單一檔名、時間戳沒帶時區、輸出位置重疊)。新指令的拒絕清單照抄該模式。
Seam 2|
assemble的 run-dir 產物。觀察點是產出的檔案:run-dir 裡的 manifest 呈現正確的等級、plan 裡只靠次級佐證成立的 item 被標記、驗證報告出現對應的逐條警告、同時有正式文件支撐的 item 不被標記。不對中間函式立 seam——manifest 由assemble重建,等級在 run-dir 產物裡就看得到,多一個 seam 只是多一處耦合要維護。必要的回歸測試:一份正式文件加一份次級佐證的 run,其原本靠單一文件歸屬的主張必須維持 supported。這是單一文件歸屬規則那條決定的守門測試,也是本功能最可能造成既有 run 破壞的地方,必須是永久保留的回歸測試。
衝突解決的正反例:正式文件與次級佐證矛盾時取正式文件且不產生衝突 issue;兩份次級佐證矛盾時仍產生衝突 issue。
既有 benchmark 的前後對照:十三案在本功能落地前後的驗證結果必須完全一致——沒有任何案例使用次級佐證,所以任何差異都是回歸。這比新增測試更能證明預設值的相容性。
Out of Scope
原始
.eml檔的解析與匯入。收件標頭、副本名單、簽名檔與附件會把個資直接倒進來源目錄,這條路徑在 grill 中被明確否決。Excel 檔的自動轉檔。試算表一律由操作者用外部工具另存為 CSV 或 Markdown 表格後再走本規格的路徑;不建轉檔器的理由與副檔名表的處理見 #98 與 #100。
PII 與密鑰偵測前移到來源風險檢查。那是本功能的前置條件而非本規格的一部分,獨立追蹤於 #101。次級佐證載體放行之前該閘必須先就位,兩者的合併時序需要協調。
端點宣告寫法的辨識放寬(#97)、
.docx與 GitBook 路徑的未驗證標註(#99)。皆為同一場 grill 的其他缺口,彼此無共同設計問題,不併入本規格。次級佐證在離線審查頁與文件品質分數裡的呈現細節。使用者故事已涵蓋需求,但具體的版面與權重留給實作時依既有慣例決定,不在本規格預先綁定。
供應商後來把某件事寫進正式文件時的自動升級。目前的預期做法是操作者移除摘錄並重跑,自動偵測升級不在本次範圍。
Further Notes
本規格的核心不變式沒有改變:供應商來源仍是規範性主張的唯一權威,來源沒說的仍然留空並記進
missing。改變的是「來源」這個詞涵蓋的載體多了一種,而那一種帶著明確的、可見的、無法被誤認的較低背書強度。這個設計刻意不模仿領域層既有的宣告支撐/推導支撐/矛盾/不足那組關係。那組關係綁在主張與片段之間,描述的是推理距離;載體可信度是來源固有的屬性,對它承載的每一條主張都成立。把後者塞進前者會讓之後沒有人分得出一個推導支撐是哪一種。
EffectiveValueAuthority是一個近似前例——它證明這個 repo 已經接受過「同一個值可以有不同權威來源」的模型——但那條軸是文件對實測,與本規格的正式對補充是不同的軸,兩者不應合併。