universal-dev-standards 6.13.1 → 6.14.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/bin/uds.js +37 -0
  2. package/bundled/ai/standards/open-work-tracking.ai.yaml +71 -4
  3. package/bundled/ai/standards/turn-completion-integrity.ai.yaml +14 -7
  4. package/bundled/core/open-work-tracking.md +111 -8
  5. package/bundled/core/turn-completion-integrity.md +58 -11
  6. package/bundled/hooks/check-turn-completion-agy.mjs +147 -0
  7. package/bundled/hooks/turn-completion/locales/en.mjs +54 -5
  8. package/bundled/hooks/turn-completion/locales/zh-TW.mjs +37 -6
  9. package/bundled/locales/zh-CN/CHANGELOG.md +33 -3
  10. package/bundled/locales/zh-CN/README.md +2 -2
  11. package/bundled/locales/zh-CN/SECURITY.md +1 -0
  12. package/bundled/locales/zh-CN/core/turn-completion-integrity.md +46 -13
  13. package/bundled/locales/zh-CN/docs/CHEATSHEET.md +6 -1
  14. package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +33 -8
  15. package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +17 -7
  16. package/bundled/locales/zh-TW/CHANGELOG.md +33 -3
  17. package/bundled/locales/zh-TW/README.md +2 -2
  18. package/bundled/locales/zh-TW/SECURITY.md +1 -0
  19. package/bundled/locales/zh-TW/core/open-work-tracking.md +88 -9
  20. package/bundled/locales/zh-TW/core/turn-completion-integrity.md +46 -13
  21. package/bundled/locales/zh-TW/docs/CHEATSHEET.md +6 -1
  22. package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +33 -8
  23. package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +17 -7
  24. package/package.json +1 -1
  25. package/src/commands/init.js +17 -2
  26. package/src/commands/open-work.js +60 -0
  27. package/src/commands/uninstall.js +1 -1
  28. package/src/commands/update.js +91 -0
  29. package/src/i18n/messages.js +3 -0
  30. package/src/installers/hooks-installer.js +276 -11
  31. package/src/uninstallers/hook-uninstaller.js +107 -4
  32. package/src/utils/detector.js +46 -1
  33. package/src/utils/open-work-tracking.mjs +693 -0
  34. package/standards-registry.json +8 -8
package/bin/uds.js CHANGED
@@ -26,6 +26,7 @@ import { compileStandards } from '../src/commands/compile.js';
26
26
  import { generateReport } from '../src/commands/report.js';
27
27
  import { mcpCommand } from '../src/commands/mcp.js';
28
28
  import { runIntentCommand } from '../src/commands/run-intent.js';
29
+ import { openWorkNextActionCommand, openWorkRevisionCommand, openWorkSeparationCommand, openWorkSelfTestCommand } from '../src/commands/open-work.js';
29
30
  import { setLanguage, setLanguageExplicit, detectLanguage, t } from '../src/i18n/messages.js';
30
31
  import { maybeCheckForUpdates, formatUpdateNotice, shouldCheckUpdateForCommand } from '../src/utils/update-checker.js';
31
32
  import { config } from '../src/utils/config-manager.js';
@@ -231,6 +232,8 @@ program
231
232
  .option('--rollback', 'Rollback to the most recent backup')
232
233
  .option('--claude-target <target>', 'Switch an existing install to a different Claude Code integration target: project (CLAUDE.md) or local (CLAUDE.local.md) — moves the UDS block, keeps your content, no reinstall')
233
234
  .option('--locale <locale>', 'Override locale for skills install (zh-tw, zh-cn, en); also reads .uds/install.yaml + UDS_LOCALE env')
235
+ .option('--with-hooks', 'Install the enforcement hooks that are missing from this already-initialized project (re-detects tools; hooks already there and your own hooks are not touched; with --plan, writes nothing; --force also overwrites edited hook scripts)')
236
+ .option('--ai-tool <tools>', 'With --with-hooks: comma-separated tools to install hooks for (claude-code, codex, gemini-cli, antigravity) instead of detecting them')
234
237
  .action(updateCommand);
235
238
 
236
239
  program
@@ -455,6 +458,40 @@ aiContextCommand
455
458
  // MCP command for AI tool integration
456
459
  mcpCommand(program);
457
460
 
461
+ // Open-work-tracking reference checks (OWT-017/018/019). A group of its own, not
462
+ // a flag on `check`: `check` validates the installed standards and has its own
463
+ // --ci/--json meaning, while these are three checks with different arguments and
464
+ // an exit-code contract in which 2 ("cannot decide") is not a pass.
465
+ const openWorkCommand = program
466
+ .command('open-work')
467
+ .description('Reference checks for open-work-tracking (OWT-017/018/019). Exit 0 no violation, 1 violation, 2 cannot decide (not a pass)');
468
+
469
+ openWorkCommand
470
+ .command('next-action [files...]')
471
+ .description('OWT-019: every "next action" field names a file path, test name, command or requirement identifier')
472
+ .option('--root <dir>', 'Directory relative paths are resolved against (default: cwd)')
473
+ .option('--id-pattern <regex>', 'Your own requirement-identifier pattern (the default is an uncalibrated initial judgment)')
474
+ .action(openWorkNextActionCommand);
475
+
476
+ openWorkCommand
477
+ .command('revision')
478
+ .description('OWT-018: a change to acceptance/goal/constraint sections needs a new, complete revision record')
479
+ .option('--file <path>', 'Carrier file (with --base)')
480
+ .option('--base <rev>', 'Git revision to compare --file against')
481
+ .option('--before <file>', 'Earlier version of the carrier (with --after)')
482
+ .option('--after <file>', 'Later version of the carrier (with --before)')
483
+ .action(openWorkRevisionCommand);
484
+
485
+ openWorkCommand
486
+ .command('separation [files...]')
487
+ .description('OWT-017: no single carrier holds both an intent section and a progress/next-action section')
488
+ .action(openWorkSeparationCommand);
489
+
490
+ openWorkCommand
491
+ .command('self-test')
492
+ .description('Run the checker\'s own self-test arms (a checker that fails them decides nothing)')
493
+ .action(openWorkSelfTestCommand);
494
+
458
495
  // uds run <intent> — language-agnostic command proxy (XSPEC-029)
459
496
  program
460
497
  .command('run <intent>')
@@ -4,22 +4,26 @@
4
4
  standard:
5
5
  id: open-work-tracking
6
6
  name: Open Work Tracking Standard
7
- description: 開放工作追蹤標準——承載開放工作的地方必須低摩擦收件、記錄解除條件、生成可推導欄位、印出分母、且在回合結束時被檢視而不阻斷
7
+ description: 開放工作追蹤標準——承載開放工作的地方必須低摩擦收件、記錄解除條件、生成可推導欄位、印出分母、在回合結束時被檢視而不阻斷、把目標與進度分開並替目標的修改留痕、且讓每個下一步點名具體對象
8
8
 
9
9
  meta:
10
- version: "1.0.0"
11
- updated: "2026-09-23"
10
+ version: "1.1.0"
11
+ updated: "2026-09-29"
12
12
  source: core/open-work-tracking.md
13
13
  description: >
14
14
  三種不同的「工作不見了」的方式(沒地方記新想法、等待中沒有解除條件、
15
15
  沒有時鐘的計畫無聲腐爛)常被塞進同一份清單,而合併本身就是失敗的一部分。
16
16
  本標準是 deferred-item-exit 的下游一半:DEX 管「延後項目有沒有離開文件」,
17
17
  本標準管「離開之後抵達的那個承載庫,自己會不會腐壞」。
18
+ 1.1.0 補兩個缺口:目標(意圖)與進度存放在不同載體、目標的修改要留痕(OWT-017/018);
19
+ 下一步要點名具體對象(OWT-019)。
18
20
 
19
21
  iron_law: >
20
22
  一個承載開放工作的地方,必須:不要求分類就能收下新項目、為每一個等待中項目記下解除條件、
21
23
  對可推導的欄位改用生成、回報還剩什麼時同時揭露看不到什麼、
22
- 在控制權從 agent 交回人的那一刻被檢視——而且那個檢視不能讓回合失敗。
24
+ 在控制權從 agent 交回人的那一刻被檢視——而且那個檢視不能讓回合失敗;
25
+ 把「這份工作為了什麼」與「做到哪了」分開存放並替前者的每次修改留下交代;
26
+ 讓每一個「下一步」都點名一個讀的人找得到的東西。
23
27
 
24
28
  # ── 寫法約束:先讀這一段,它拘束下面每一條 ────────────────────────────────
25
29
  # 與 deferred-item-exit 受的是同一條約束(DEC-049),套用在下游一層:
@@ -33,12 +37,14 @@ standard:
33
37
  - "「這是最新的」這句宣稱必須從什麼可被證明"
34
38
  - "一個回報數字與它看不到的部分之間的關係"
35
39
  - "控制權交回人的那一點存在一個確認點,且它永不阻斷"
40
+ - "一次目標的修改與說明它的修訂紀錄之間必須存在的關係"
36
41
  inadmissible_how:
37
42
  - "收件點是哪個 app、檔案或工單系統"
38
43
  - "輪詢解除條件的排程器或機器人"
39
44
  - "具體用哪個雜湊函式、diff 工具或 CI 供應商"
40
45
  - "儀表板版面或報告範本"
41
46
  - "用什麼 hook 系統、shell 或 cron 實作確認點"
47
+ - "哪一種版本控制系統、核可工具或檔案配置承載目標或其修訂紀錄"
42
48
  consequence: >
43
49
  本標準不附帶任何閘門。它只說一個承載開放工作的地方必須具備什麼性質;
44
50
  有沒有東西在檢查是採用專案的決定,而 OWT-014/OWT-015 是用來讓那個決定
@@ -58,6 +64,10 @@ standard:
58
64
  - "措辭清單可以補充結構欄位,但涵蓋率必須明示未知,乾淨結果不得回報成「沒有漏掉」"
59
65
  - "超過門檻仍未分類的項目要在摘要裡被點名,不得被合併進一個總數"
60
66
  - "項目從承載庫消失(drop)必須帶一句理由,不得只是消失"
67
+ - "目標/驗收條件/限制(意圖)與進度/下一步存在不同的載體;判準是結構(小節、欄位、型別化標記),不是檔名——更新進度不得需要碰目標"
68
+ - "目標或驗收條件每次被修改都留下「改了什麼、誰核可、為什麼」;沒有核可者的修改要在控制權交回人時列出來,不得靜默,但列出只是回報、永不阻斷"
69
+ - "「下一步」欄位至少點名一個具體對象——檔案路徑、測試名稱、指令或需求編號之一;只有動詞不算。這是結構判準,不判斷措辭好壞"
70
+ - "點名對象的辨認本身是樣式比對,涵蓋率明示為未知;回報三態(點名且已找到/點名但未找到/未點名),只有未點名是違反"
61
71
 
62
72
  # ── R5 vs turn-completion-integrity:掛同一事件,行為刻意相反 ──────────────
63
73
  vs_turn_completion_integrity:
@@ -122,6 +132,12 @@ standard:
122
132
  why: "正確到某個項目用清單沒預料到的方式寫出來為止"
123
133
  - pattern: "項目從承載庫無聲消失"
124
134
  why: "與一個弄丟它的 bug 無從分辨"
135
+ - pattern: "目標或驗收條件被改了,卻沒有「誰同意」的紀錄"
136
+ why: "「每一條驗收都滿足」依然為真,只是針對另一組條件;被改過的目標讀起來像一直這麼寫"
137
+ - pattern: "把手寫的狀態檔當成狀態真相"
138
+ why: "會過期,而比過期內容新的戳看不見"
139
+ - pattern: "下一步寫「繼續實作」或「處理剩下的」"
140
+ why: "沒有點名任何可以開始的東西;與被遺忘的項目無從分辨"
125
141
 
126
142
  rules:
127
143
  - id: OWT-001
@@ -172,10 +188,55 @@ standard:
172
188
  - id: OWT-016
173
189
  rule: "本標準各要求所引用的任何窗口或閾值,須載明來歷,或標為未校準"
174
190
  severity: warning
191
+ - id: OWT-017
192
+ rule: "承載一件工作之目標/驗收條件/限制的載體,不同時承載它的進度或下一步,反之亦然;以走訪各載體的結構欄位判定,不看檔名;更新進度因此不需要碰目標"
193
+ severity: warning
194
+ severity_rationale: "分開存放只是手段:分開了卻沒有修訂紀錄(OWT-018)照樣漏,單一載體配上嚴格修訂紀錄照樣達成 OWT-018 的目的,所以違反的傷害是間接的"
195
+ - id: OWT-018
196
+ rule: "對一件工作的目標/驗收條件/限制的每一次修改,都留下「改了什麼、誰核可、為什麼」的紀錄;沒有核可者的修改須在控制權交回人時列出(OWT-007),不得靜默"
197
+ severity: error
198
+ severity_rationale: "失效是靜默的,而它只有一個時刻能被抓到——修改發生的那一刻;事後只剩被改過的文字可讀"
199
+ - id: OWT-019
200
+ rule: "「下一步」欄位至少點名一個具體對象:檔案路徑、測試名稱、指令或需求編號之一;只有動詞不算。判定的是有沒有點名對象,不是措辭好壞"
201
+ severity: warning
202
+ severity_rationale: "「有點名對象」的結構檢查必然粗糙(被點名的對象仍可能無關),違反的代價是下一個工作階段的時間,不是工作本身"
203
+
204
+ # ── 意圖 vs 進度(OWT-017/018/019)──────────────────────────────────────
205
+ intent_vs_progress:
206
+ intent: "目標、驗收條件、限制——說「完成」是什麼意思;很少改,只因某人決定而改"
207
+ progress: "做到哪、還剩什麼、下一步、卡在哪——每個工作階段都在變,由做事的 agent 寫"
208
+ form: "不限:規格檔搭配工作紀錄、或目標/狀態兩個檔都符合;判準是結構不是檔名"
209
+ revision_record:
210
+ fields: [what_changed, approver, reason]
211
+ no_approver: "不禁止(agent 正當地會提出修改);但要在控制權交回人時列出,且永不阻斷(OWT-008)"
212
+ decidable_by_a_check: "意圖有沒有變、有沒有新增一筆紀錄、紀錄是否完整、核可者欄位有沒有填"
213
+ not_decidable_by_a_check: "那筆紀錄是否誠實描述了這次修改——這是語意宣稱,不是 artefact 之間的關係(OWT-014)"
214
+ next_action:
215
+ names_one_of: [file_path, test_name, command, requirement_identifier]
216
+ outcomes:
217
+ - named_and_resolved
218
+ - named_unresolved
219
+ - unnamed # 唯一的違反
220
+ coverage: "辨認路徑/指令/測試名稱/編號本身是樣式比對,涵蓋率未知(OWT-011);認不出的格式回報為未點名"
221
+
222
+ # ── 刻意不採納(來源:使用者轉貼的提示詞,作者不明,僅借設計形狀)──────────
223
+ not_adopted:
224
+ source: "使用者轉貼的提示詞,作者與出處不明、無實作;僅借設計形狀,不引用其任何宣稱"
225
+ items:
226
+ - what: "以手寫狀態檔作為狀態真相"
227
+ why: "手寫狀態會過期,而戳比過期內容新是隱形的(OWT-004/005 的起因);只說以版本控制為準卻沒有對帳機制,就是採納一個已知會過期的來源。OWT-003 已要求可推導的欄位改用生成"
228
+ - what: "固定的開工儀式(讀檔→查版本控制→驗證)"
229
+ why: "交接點已由 OWT-007 與 turn-completion-integrity 管住;開工儀式要靠各代理工具自己的指示設定,寫進標準只會得到一條沒有任何 artefact 檢查判定得了的要求(違反 OWT-014)"
175
230
 
176
231
  # ── 本標準在 UDS 側沒有閘門,這件事被記錄而非被暗示 ──────────────────────
177
232
  enforcement:
178
233
  automated_gate: false
234
+ reference_check:
235
+ command: "uds open-work <next-action|revision|separation|self-test>" # 隨 npm 安裝包出貨,採用者不需要 clone
236
+ path: cli/src/utils/open-work-tracking.mjs # 規則本體只有這一份
237
+ repo_entry: scripts/check-open-work-tracking.mjs # 只在 UDS repo 副本裡的薄殼,不含規則
238
+ covers: [OWT-017, OWT-018, OWT-019]
239
+ status: "參考判定程序,作為 OWT-015 意義上的證據(已對違反樣本紅過);不是閘門,未接進任何 UDS 發版閘門,因為 UDS 沒有承載開放工作的地方可供檢查"
179
240
  why_not: >
180
241
  依上面的寫法約束,UDS 陳述關係,有沒有東西判定它是採用專案的決定。
181
242
  UDS 不出貨承載開放工作的地方本身,只出貨要求它具備什麼性質的標準。
@@ -192,6 +253,9 @@ standard:
192
253
  age_at_writing: "以小時計,單一使用者,單一 repo"
193
254
  what_this_supports: "R1–R6 的形狀——每一條都對應一個當天觀察到的失效或使用者原話描述的模式"
194
255
  what_this_does_not_support: "OWT-001 的「兩個欄位」與 OWT-012 的「兩週」這類具體閾值——這些是初始判斷,不是量測"
256
+ added_in_1_1_0:
257
+ origin: "DEC-122(2026-09-29):一個採用專案裡,目標與進度沒分開、且一條驗收條件被改而無人核可紀錄;設計形狀借自使用者轉貼、作者不明的提示詞"
258
+ uncalibrated_per_owt_016: "參考判定程序用的標題詞彙、指令名清單、副檔名清單、需求編號樣式——全是初始判斷,沒有對照真實使用量測;採用專案應傳入自己的"
195
259
  recalibration: "由採用專案自行決定與自行訂定時程;本標準不承諾覆核日期,如同它不承諾閘門"
196
260
 
197
261
  related:
@@ -214,3 +278,6 @@ physical_spec:
214
278
  - "任何「還有 N 項」數字旁的看見/看不見分母"
215
279
  - "掛在回合結束、永不阻斷的開放工作摘要"
216
280
  - "超過門檻仍未分類項目的個別點名,與已丟棄項目的一句理由"
281
+ - "目標/驗收條件與進度/下一步分屬不同載體(以結構判定)"
282
+ - "目標或驗收條件的每次修改對應一筆「改了什麼、誰核可、為什麼」紀錄;無核可者的修改在交回點被列出"
283
+ - "每個「下一步」點名至少一個檔案路徑、測試名稱、指令或需求編號"
@@ -3,8 +3,8 @@
3
3
 
4
4
  id: turn-completion-integrity
5
5
  meta:
6
- version: "1.4.1"
7
- updated: "2026-09-28"
6
+ version: "1.5.0"
7
+ updated: "2026-09-29"
8
8
  source: core/turn-completion-integrity.md
9
9
  description: An agent must not end a turn having stated a next action it did not take; enforced at turn end, not by instruction
10
10
  related:
@@ -119,9 +119,9 @@ enforcement:
119
119
 
120
120
  # The check is enforced only where an adapter exists AND a hook is wired into
121
121
  # that harness's own config. This is separate from `enforcement:` above, which
122
- # only describes the Claude Code entry the generic installer walks — Codex and
123
- # Gemini CLI are installed by their own installCodexHooks/installGeminiHooks
124
- # functions (cli/src/installers/hooks-installer.js), each writing that
122
+ # only describes the Claude Code entry the generic installer walks — Codex,
123
+ # Gemini CLI and Antigravity CLI are installed by their own
124
+ # installCodexHooks/installGeminiHooks/installAgyHooks functions (cli/src/installers/hooks-installer.js), each writing that
125
125
  # harness's own config file and output contract.
126
126
  supported_harnesses:
127
127
  - harness: claude-code
@@ -144,8 +144,13 @@ supported_harnesses:
144
144
  status: legacy
145
145
  reason: "Google retired Gemini CLI for personal accounts on 2026-06-18 in favour of Antigravity CLI; enterprise accounts keep both. Adapter kept for them; never confirmed against a real Gemini CLI session"
146
146
  - harness: antigravity-cli
147
- status: not-yet-supported
148
- reason: "Documented Stop contract differs: config .agents/hooks.json, payload has only transcriptPath (no final or human message), block is {\"decision\":\"continue\",\"reason\":...}. Adapter waits until that contract is observed against a real session (R3)"
147
+ event: Stop
148
+ config_file: ".agents/hooks.json"
149
+ block_shape: '{"decision":"continue","reason":...}, exit 0; {} allows'
150
+ script: scripts/hooks/check-turn-completion-agy.mjs
151
+ contract: "Config is keyed by hook name ({name:{Stop:[{type,command,timeout}]}}, timeout in seconds). stdin carries only transcriptPath + metadata, so the final reply (last MODEL/PLANNER_RESPONSE) and the human's message (last USER_EXPLICIT/USER_INPUT, unwrapped from <USER_REQUEST>) are both read from the transcript. SYSTEM_MESSAGE records must never be read as the human: the hook's own continue reason comes back as one."
152
+ verified: "agy 1.2.12, 2026-09-29: non-interactive `agy -p`, single turn, no tool calls; transcript already holds the final reply when the hook runs; continue is honoured; no trust prompt encountered; the hook's working directory is `.agents/` (NOT the project root), so the installed command is `node ../scripts/hooks/check-turn-completion-agy.mjs`, and agy silently lets a hook that fails to start through"
153
+ not_verified: "multi-turn; turns with tool calls; fullyIdle false; non-empty error; interactive mode; whether .agents/hooks.json needs a registered Antigravity project; whether the working directory is always .agents/ (measured for a project-level file only)"
149
154
  - harness: cursor
150
155
  status: evaluated-not-supported
151
156
  reason: "Whether Cursor's stop hook can actually block a turn was unresolved as of writing; shipping an adapter against an unverified contract repeats the exact failure R3 exists to prevent"
@@ -167,3 +172,5 @@ checklist:
167
172
  - The attribution search excludes the check's own headings and scaffolding
168
173
  - Each supported harness's block contract is verified against that harness's own docs, not assumed from another harness
169
174
  - The installer only writes a harness's hook config for a harness the adopter selected
175
+ - A harness whose transcript records system-written messages reads the human's message from the human's records only, never from anything that is not the model
176
+ - A harness contract is shipped only for the range observed against a real session, and the unobserved range is stated
@@ -2,8 +2,8 @@
2
2
 
3
3
  > **Language**: English | [繁體中文](../locales/zh-TW/core/open-work-tracking.md)
4
4
 
5
- **Version**: 1.0.0
6
- **Last Updated**: 2026-09-23
5
+ **Version**: 1.1.0
6
+ **Last Updated**: 2026-09-29
7
7
  **Applicability**: Any project that carries work across more than one working session and risks losing an item between them
8
8
  **Scope**: universal
9
9
 
@@ -35,9 +35,10 @@ Three distinct ways work goes missing between sessions are routinely folded into
35
35
  | 某項目因等待別的事件而暫停 | 「等待中」若沒有記錄解除條件,與「被忘記」無法分辨 | 與等待一起記錄的解除條件 |
36
36
  | 已規劃的項目還沒動工,時間過去 | 沒有時鐘的項目會無聲腐爛——沒有東西會再指向它 | 一個門檻,或一次被迫的定期檢視,讓它重新浮現 |
37
37
 
38
- Each requirement below traces to one row of that table, or to one of four failures observed the day this standard's design was drafted (see [Evidence and calibration](#evidence-and-calibration)). **None of the mechanisms is prescribed** — per the same constraint [deferred-item-exit](deferred-item-exit.md) states for its own exits, and for the same reason (DEC-049: UDS defines relations that must hold, adoption layers choose what maintains them).
38
+ Each requirement below traces to one row of that table, to one of four failures observed the day this standard's design was drafted, or (OWT-017–OWT-019) to two gaps found in 1.1.0 (see [Evidence and calibration](#evidence-and-calibration)). **None of the mechanisms is prescribed** — per the same constraint [deferred-item-exit](deferred-item-exit.md) states for its own exits, and for the same reason (DEC-049: UDS defines relations that must hold, adoption layers choose what maintains them).
39
39
 
40
- 下面每一條要求都對應這張表的一列,或對應本標準設計當天觀察到的四個失效之一
40
+ 下面每一條要求都對應這張表的一列、對應本標準設計當天觀察到的四個失效之一,
41
+ 或(OWT-017–OWT-019)對應 1.1.0 補上的兩個缺口
41
42
  (見〈[證據與校準](#evidence-and-calibration)〉)。**沒有任何一個機制被規定**——
42
43
  理由與 [deferred-item-exit](deferred-item-exit.md) 對自己出口的約束相同(DEC-049:
43
44
  UDS 定義必須成立的關係,維持它的機制由採用層選擇)。
@@ -61,6 +62,7 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
61
62
  | A property a "this is current" claim must be provable from | A specific hash function, diff tool, or CI provider |
62
63
  | A relation between a reported count and what it could not see | A dashboard layout or report template |
63
64
  | That a checkpoint exists at the point control returns to a human, and never blocks | Which hook system, shell, or cron implements it |
65
+ | A relation between an edit to a goal and the revision record that accounts for it | Which version-control system, approval tool, or file layout holds either |
64
66
 
65
67
  | 這裡容許——**what** | 這裡不容許——**how** |
66
68
  |---|---|
@@ -69,6 +71,7 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
69
71
  | 「這是最新的」這句宣稱必須從什麼可被證明 | 具體用哪個雜湊函式、diff 工具或 CI 供應商 |
70
72
  | 一個回報數字與它看不到的部分之間的關係 | 儀表板版面或報告範本 |
71
73
  | 控制權交回人的那一點存在一個確認點,且它永不阻斷 | 用什麼 hook 系統、shell 或 cron 實作它 |
74
+ | 一次目標的修改與說明它的修訂紀錄之間必須存在的關係 | 哪一種版本控制系統、核可工具或檔案配置承載其中任何一個 |
72
75
 
73
76
  **Consequence, stated plainly**: this standard ships **no gate**. It says what must be true of a carrier for open work. Whether anything checks it is the adopting project's decision — [OWT-014](#requirements) and [OWT-015](#requirements) exist so that decision cannot be made silently.
74
77
 
@@ -80,11 +83,13 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
80
83
 
81
84
  ## The invariant
82
85
 
83
- **A carrier of open work must (1) accept a new item without demanding classification, (2) record a release condition for every item it marks waiting, (3) generate any field a reliable source already determines, (4) disclose what it cannot see whenever it reports what remains, and (5) be checked at the moment control returns from agent to human — by something that cannot fail the turn.**
86
+ **A carrier of open work must (1) accept a new item without demanding classification, (2) record a release condition for every item it marks waiting, (3) generate any field a reliable source already determines, (4) disclose what it cannot see whenever it reports what remains, (5) be checked at the moment control returns from agent to human — by something that cannot fail the turn, (6) keep what the work is *for* apart from how far it has got, account for every edit to the former, and (7) make every "next action" name something a reader can go and find.**
84
87
 
85
88
  **一個承載開放工作的地方,必須:(1)不要求分類就能收下新項目、(2)為每一個標為等待中的項目記下解除條件、
86
89
  (3)對任何有可靠來源可推導的欄位改用生成、(4)回報還剩什麼時同時揭露看不到什麼、
87
- (5)在控制權從 agent 交回人的那一刻被檢視——而且那個檢視不能讓回合失敗。**
90
+ (5)在控制權從 agent 交回人的那一刻被檢視——而且那個檢視不能讓回合失敗、
91
+ (6)把「這份工作為了什麼」與「做到哪了」分開存放,並替前者的每一次修改留下交代、
92
+ (7)讓每一個「下一步」都點名一個讀的人找得到的東西。**
88
93
 
89
94
  ---
90
95
 
@@ -108,6 +113,9 @@ UDS 定義**活動**,採用層負責**編排**(DEC-049)。一份寫成工
108
113
  | **OWT-014** | Every requirement of this standard is expressible as a decidable relation over named artefacts. A requirement that cannot be so expressed does not belong in this standard | error |
109
114
  | **OWT-015** | A check offered as evidence for any requirement here has been observed to report failure against a sample built to violate it. A check never observed red is not admissible evidence | error |
110
115
  | **OWT-016** | Any window or threshold this standard's requirements reference carries its provenance, or is marked uncalibrated | warning |
116
+ | **OWT-017** | A carrier that holds a piece of work's goal, acceptance criteria, or constraints holds none of its progress or next action, and the reverse. This is decided by walking each carrier's structural fields (sections, columns, typed markers), never by file names. A progress update therefore never requires touching the goal | warning |
117
+ | **OWT-018** | Every change to a piece of work's goal, acceptance criteria, or constraints leaves a revision record stating what changed, who approved it, and why. A change with no approver is listed when control returns to a human (OWT-007); it is never silent | error |
118
+ | **OWT-019** | A "next action" field names at least one concrete object: a file path, a test name, a command, or a requirement identifier. A verb alone ("continue", "handle the rest") does not. This judges whether an object is named, never how well the sentence is worded | warning |
111
119
 
112
120
  ---
113
121
 
@@ -219,6 +227,78 @@ A free-text wording scan ("contains the phrase 'waiting on'") can legitimately s
219
227
 
220
228
  ---
221
229
 
230
+ ## Intent and progress are two different kinds of fact
231
+
232
+ **Intent** — the goal, the acceptance criteria, the constraints — says what "done" means. It changes rarely, and only because someone decided it should. **Progress** — where the work stands, what remains, the next action, what is blocking — changes every session and is the working agent's to write. When both live in one carrier, every routine progress update is an edit to the very document that defines "done", and a change to the definition cannot be told apart, in review or in a diff, from bookkeeping. **OWT-017** separates them. The form is free: a specification beside a work log, or a two-file goal/state pair, both satisfy it. **The test is structure, not file names** — walk each carrier's sections, columns, and typed markers, and ask whether one carrier holds both kinds.
233
+
234
+ **意圖**——目標、驗收條件、限制——說的是「完成」是什麼意思。它很少改,而且只在有人決定要改時才改。
235
+ **進度**——做到哪、還剩什麼、下一步、卡在哪——每個工作階段都在變,由做事的 agent 來寫。
236
+ 兩者住在同一個載體時,每一次例行的進度更新,都是在編輯那份定義「完成」的文件本身,
237
+ 而「定義被改了」在審查裡、在 diff 裡,都與日常記帳分不出來。**OWT-017** 把它們分開。
238
+ 形式不限:規格檔搭配工作紀錄、或目標/狀態兩個檔,都符合。**判準是結構,不是檔名**——
239
+ 走訪每個載體的小節、欄位與型別化標記,問同一個載體是否同時裝著兩種東西。
240
+
241
+ **OWT-018** names the failure that separation alone does not prevent: an acceptance criterion is revised in the middle of the work, and nothing records who agreed. Afterwards "every criterion is met" is true — of a different set of criteria. It is the same shape as a fresh stamp over stale content: the edited goal reads exactly like a goal that always said that. So every change to intent leaves a record of **what changed, who approved it, and why**. A change with no approver is not forbidden — an agent legitimately proposes changes, and forbidding them only teaches it to make them silently — but it is **listed when control returns to a human** (OWT-007). Like everything attached to that event, the listing reports; it never blocks (OWT-008). What a check can decide here is decidable and no more: that the intent changed, that a new record exists, that the record is complete, and whether its approver is filled in. **It cannot decide whether the record honestly describes the change** — that is a claim about meaning, not a relation over artefacts (OWT-014), and this standard does not pretend otherwise.
242
+
243
+ **OWT-018** 點名的,是光靠分開存放防不了的失效:工作進行到一半,某條驗收條件被改了,
244
+ 而沒有任何紀錄說明誰同意過。事後「每一條驗收都滿足了」依然為真——只是針對另一組條件。
245
+ 它與「新戳蓋在舊內容上」是同一個形狀:被改過的目標,讀起來與一個一直這麼寫的目標一模一樣。
246
+ 所以每一次對意圖的修改,都要留下**改了什麼、誰核可、為什麼**的紀錄。沒有核可者的修改並不被禁止——
247
+ agent 正當地會提出修改,禁止只會教它學會靜默地改——但它要在**控制權交回人的那一刻被列出來**(OWT-007)。
248
+ 如同掛在那個事件上的一切,列出只是回報,永不阻斷(OWT-008)。這裡檢查能判定的就只有可判定的部分:
249
+ 意圖有沒有變、有沒有新增一筆紀錄、那筆紀錄是否完整、核可者欄位有沒有填。
250
+ **它判定不了那筆紀錄是否誠實描述了這次修改**——那是關於語意的宣稱,不是 artefact 之間的關係(OWT-014),
251
+ 本標準不假裝它做得到。
252
+
253
+ **Why these severities.** OWT-018 is an `error` because the failure is silent and there is exactly one moment it can be caught — when the edit happens; afterwards the edited text is all anyone can read. OWT-017 is a `warning` because separation is a means: separated carriers with no revision record (OWT-018) still leak, and one carrier with a strict revision record still serves OWT-018's purpose, so the harm of a violation is indirect. OWT-019 is a `warning` because a structural check for "names an object" is necessarily coarse — a named object can still be irrelevant — and a violation costs the next session time rather than costing the work.
254
+
255
+ **這些嚴重度的理由。** OWT-018 是 `error`,因為這個失效是靜默的,而它只有一個時刻能被抓到——修改發生的那一刻;
256
+ 事後所有人能讀到的,就只剩被改過的文字。OWT-017 是 `warning`,因為分開存放只是手段:
257
+ 分開了卻沒有修訂紀錄(OWT-018)照樣漏,單一載體配上嚴格的修訂紀錄照樣達成 OWT-018 的目的,
258
+ 所以違反的傷害是間接的。OWT-019 是 `warning`,因為「有點名對象」的結構檢查必然粗糙——
259
+ 被點名的對象仍可能無關——而違反的代價是下一個工作階段的時間,不是工作本身。
260
+
261
+ ---
262
+
263
+ ## A next action that names nothing is a mood
264
+
265
+ "Continue the implementation" and "handle the rest" cannot be told apart from a forgotten item: the next session has nothing to start from and must re-derive where the work stood, which is the cost this whole standard exists to avoid. **OWT-019** requires the "next action" field to name at least one object a reader can go and find — a file path, a test name, a command, or a requirement identifier. It is a **structural** test (is an object named?), not a judgment about wording; a well-phrased sentence that names nothing still fails, and a terse one that names a test passes. That keeps it inside OWT-010 and OWT-014.
266
+
267
+ 「繼續實作」與「處理剩下的」,與一個被遺忘的項目分不出來:下一個工作階段沒有起點可以開始,
268
+ 得重新推導工作停在哪——而那正是本標準整個存在要避免的成本。**OWT-019** 要求「下一步」欄位
269
+ 至少點名一個讀的人找得到的對象——檔案路徑、測試名稱、指令、或需求編號。
270
+ 它是**結構**判準(有沒有點名對象),不是措辭好壞的判斷;措辭漂亮但什麼都沒點名的句子照樣不過,
271
+ 簡短但點了一個測試名稱的句子照樣過。這讓它留在 OWT-010 與 OWT-014 的範圍之內。
272
+
273
+ A check reports three outcomes, never one green: **named and resolved** (the object was found — for instance the path exists), **named, unresolved** (an object is named but could not be found — legitimate when the next action is to create it), and **unnamed** (a violation). Recognising *that* a string is a path, a command, a test name, or an identifier is itself a pattern match, so per OWT-011 its coverage is declared unknown: an unrecognised format is reported as unnamed, and a clean pass never means "every next action is specific".
274
+
275
+ 檢查回報三種結果,而不是一個綠燈:**點名且已找到**(對象被找到——例如路徑存在)、**點名但未找到**
276
+ (有點名對象但找不到——當下一步就是要建立它時是正當的)、**未點名**(違反)。
277
+ 辨認「這串字是路徑、指令、測試名稱還是編號」本身是樣式比對,所以依 OWT-011 其涵蓋率明示為未知:
278
+ 認不出的格式會被回報為未點名,而乾淨的通過絕不表示「每個下一步都夠具體」。
279
+
280
+ ---
281
+
282
+ ## What this standard deliberately does not adopt
283
+
284
+ The two additions above were prompted by a prompt a user forwarded, **whose author and provenance are unknown and which ships no implementation**. Only its design shapes were borrowed; none of its claims is cited here. The rest of what it proposes was examined and **not** adopted, for reasons about mechanism rather than taste:
285
+
286
+ | Not adopted | Why (mechanism) |
287
+ |---|---|
288
+ | A hand-written state file as the source of truth | A hand-written state goes stale, and a stamp newer than stale content is invisible (the failure OWT-004 and OWT-005 exist for). Saying "trust version control when they disagree" without a mechanism that reconciles the file with version control adopts a known-stale source. OWT-003 already requires derivable fields to be generated |
289
+ | A fixed start-of-work ritual (read the files, then check version control, then verify) | The hand-off points are already governed: OWT-007 at turn end, and [turn-completion-integrity](turn-completion-integrity.md). A start-of-work ritual is configured in each agent tool's own instructions; writing it here yields a requirement no check over an artefact can decide, which OWT-014 excludes |
290
+
291
+ 上面兩項新增,起因是使用者轉貼的一份提示詞——**作者與出處不明,也沒有任何實作**。
292
+ 只借了它的設計形狀,本文不引用它的任何宣稱。它提出的其餘部分經過檢視、**沒有**採納,
293
+ 理由是機制層的,不是口味:
294
+
295
+ | 不採納 | 理由(機制層) |
296
+ |---|---|
297
+ | 以手寫狀態檔作為狀態真相 | 手寫狀態會過期,而「戳比過期內容新」是隱形的(OWT-004、OWT-005 存在的起因)。只說「兩者不一致時以版本控制為準」,卻沒有任何機制讓該檔與版本控制對帳,就是採納一個已知會過期的來源。OWT-003 已經要求可推導的欄位改用生成 |
298
+ | 固定的開工儀式(讀檔→查版本控制→驗證) | 交接點已被管住:回合結束有 OWT-007,另有 [turn-completion-integrity](turn-completion-integrity.md)。開工儀式要靠各代理工具自己的指示來設定;寫進這裡只會得到一條沒有任何 artefact 上的檢查判定得了的要求,而那正是 OWT-014 排除的東西 |
299
+
300
+ ---
301
+
222
302
  ## A requirement that cannot be checked is not a requirement here
223
303
 
224
304
  **OWT-014** is a constraint on this standard's own contents, the same role [deferred-item-exit](deferred-item-exit.md)'s DEX-003 plays for that standard. Every requirement above names artefacts and a relation between them that a reader — or something a project builds — can decide. A property this standard cared about but could not phrase this way was left out of the table rather than included as an unenforceable aspiration. One example: "the capture point actually gets used" is exactly the outcome OWT-001 exists to protect, but it is a claim about human behavior over time, not a decidable relation over an artefact at a point in time — so it is stated here, in prose, as the *reason* for OWT-001, and is not itself a numbered requirement.
@@ -263,6 +343,9 @@ DEX-003 扮演的角色相同。上面每一條都指名了 artefact 與它們
263
343
  | A checkpoint that blocks the turn on "some work remains" | Fires on every turn; a gate that is always true gets disabled, and then protects nothing |
264
344
  | Triage status read only from prose wording | Correct until an item is phrased a way the wording list did not anticipate |
265
345
  | An item that silently vanishes from the carrier | Indistinguishable from a bug that lost it |
346
+ | A goal or acceptance criterion edited with no record of who agreed | "Every criterion is met" stays true, of a different set of criteria; the edited goal reads as if it always said that |
347
+ | A hand-written state file treated as the source of truth | Goes stale, and a stamp newer than the stale content cannot be seen |
348
+ | A next action of "continue implementation" or "handle the rest" | Names nothing to start from; indistinguishable from a forgotten item |
266
349
 
267
350
  | 反模式 | 為什麼會失敗 |
268
351
  |---|---|
@@ -275,16 +358,22 @@ DEX-003 扮演的角色相同。上面每一條都指名了 artefact 與它們
275
358
  | 確認點擋住回合結束、理由是「還有工作沒做完」 | 每個回合都會觸發;永遠為真的閘門會被關掉,關掉之後什麼都不保護 |
276
359
  | 分類狀態只靠散文措辭判讀 | 正確到某個項目用清單沒預料到的方式寫出來為止 |
277
360
  | 項目從承載庫裡無聲消失 | 與一個弄丟它的 bug 無從分辨 |
361
+ | 目標或驗收條件被改了,卻沒有任何「誰同意」的紀錄 | 「每一條驗收都滿足」依然為真,只是針對另一組條件;被改過的目標讀起來像一直這麼寫 |
362
+ | 把手寫的狀態檔當成狀態真相 | 會過期,而比過期內容新的戳看不見 |
363
+ | 下一步寫「繼續實作」或「處理剩下的」 | 沒有點名任何可以開始的東西;與被遺忘的項目無從分辨 |
278
364
 
279
365
  ---
280
366
 
281
367
  ## What enforces this standard
282
368
 
283
- **Nothing in UDS does, and that is recorded rather than implied.** UDS states the relations a carrier of open work must satisfy; whether anything decides them is the adopting project's call, per the [writing constraint](#how-this-standard-is-written--and-why-it-is-written-that-way) above — the same boundary [deferred-item-exit](deferred-item-exit.md) draws for its own exits.
369
+ **Nothing in UDS gates on it, and that is recorded rather than implied.** UDS states the relations a carrier of open work must satisfy; whether anything decides them is the adopting project's call, per the [writing constraint](#how-this-standard-is-written--and-why-it-is-written-that-way) above — the same boundary [deferred-item-exit](deferred-item-exit.md) draws for its own exits. Since 1.1.0 UDS does ship one **reference decision procedure** for OWT-017–OWT-019 — `uds open-work next-action | revision | separation` from the npm package (`uds open-work self-test` runs the checker's own arms; from a clone of the UDS repository `node scripts/check-open-work-tracking.mjs` runs the same code) — offered as evidence in the OWT-015 sense — it has been observed to fail against violating samples — for an adopter to run or to reimplement. It is not wired into any UDS release gate, because UDS carries no open-work carrier for it to check.
284
370
 
285
- **本標準沒有任何 UDS 側的閘門,而這件事是被記錄的,不是被暗示的。** UDS 陳述一個承載開放工作的地方
371
+ **UDS 不對本標準設任何閘門,而這件事是被記錄的,不是被暗示的。** UDS 陳述一個承載開放工作的地方
286
372
  必須滿足的關係;有沒有東西去判定它,依上面的[寫法約束](#how-this-standard-is-written--and-why-it-is-written-that-way),
287
373
  是採用專案的決定——與 [deferred-item-exit](deferred-item-exit.md) 對自己出口劃的界線相同。
374
+ 自 1.1.0 起,UDS 為 OWT-017–OWT-019 附上一支**參考判定程序**(`scripts/check-open-work-tracking.mjs`),
375
+ 作為 OWT-015 意義上的證據——它已被觀察到對違反的樣本回報失敗——供採用者直接執行或自行重做。
376
+ 它沒有接進任何 UDS 發版閘門,因為 UDS 本身沒有承載開放工作的地方可供它檢查。
288
377
 
289
378
  What this standard does do is make that call visible: OWT-014 guarantees every requirement here **can** be decided, OWT-015 fixes what it takes for a decision to count, and OWT-005/OWT-011 fix what a partial decision is allowed to print.
290
379
 
@@ -305,12 +394,23 @@ This standard's shape comes from one adopting project's observations made and ac
305
394
  - **OWT-001's "no more than two fields"** and **OWT-012's "past the declared threshold"** (illustrated at two weeks in the originating observation) are **initial judgments, not measurements** — per OWT-016. No controlled comparison exists yet between two fields and three, or between a two-week and a four-week unclassified threshold.
306
395
  - Recalibrating either number against real usage, or downgrading either into project-specific guidance, is the adopting project's decision to make and to date — this standard does not carry that commitment, the same way it carries no gate.
307
396
 
397
+ **1.1.0's additions (OWT-017–OWT-019)** come from two gaps found in one adopting project on 2026-09-29 (DEC-122): the standard said nothing about separating a work item's goal from its progress, and an acceptance criterion in one of that project's specifications was revised mid-work with no record of who agreed. The design shapes were borrowed from a prompt a user forwarded, author unknown (see [What this standard deliberately does not adopt](#what-this-standard-deliberately-does-not-adopt)). The reference procedure is hours old, has one author, and has run against constructed samples, not against a real backlog of revisions. Under OWT-016, everything it uses that resembles a threshold is **uncalibrated, an initial judgment**: the heading vocabulary that marks a section as intent, progress, next action, or revision record; the list of command names it recognises; the file-extension list; and the requirement-identifier pattern. None of them was measured against real usage, and a project should pass its own.
398
+
308
399
  - **OWT-001 的「不超過兩個欄位」**與**OWT-012 的「過了宣告的門檻」**(在原始觀察中以兩週為例)
309
400
  依 OWT-016 是**初始判斷,不是量測結果**——兩個欄位跟三個欄位、兩週跟四週的未分類門檻,
310
401
  目前都沒有對照比較過。
311
402
  - 依實際使用情況重新校準這兩個數字、或將其中任一個降級為專案特定指引,是採用專案自己的決定
312
403
  與自己的時程——本標準不承諾這件事,如同它不附帶閘門一樣。
313
404
 
405
+ **1.1.0 的新增(OWT-017–OWT-019)**來自 2026-09-29 在一個採用專案裡發現的兩個缺口(DEC-122):
406
+ 本標準對「把工作項目的目標與進度分開」沒有任何說法,而該專案某份規格裡的一條驗收條件,
407
+ 在工作進行到一半時被修改,沒有任何紀錄說明誰同意過。設計形狀借自使用者轉貼的一份提示詞,作者不明
408
+ (見〈[本標準刻意不採納的東西](#what-this-standard-deliberately-does-not-adopt)〉)。
409
+ 那支參考判定程序只有幾小時大、只有一位作者,跑過的是人造樣本,不是真實的修訂歷史。
410
+ 依 OWT-016,它用到的一切類似閾值的東西都是**未校準、初始判斷**:把某個小節認作意圖、進度、下一步、
411
+ 或修訂紀錄的標題詞彙;它認得的指令名清單;副檔名清單;需求編號的樣式。
412
+ 沒有任何一項對照過真實使用量測,採用專案應傳入自己的。
413
+
314
414
  ---
315
415
 
316
416
  ## Relationship to other standards
@@ -319,6 +419,7 @@ This standard's shape comes from one adopting project's observations made and ac
319
419
  - [turn-completion-integrity](turn-completion-integrity.md) — attaches to the same event (turn end) and is built to behave oppositely: TCI blocks on a rare, specific abandoned commitment; this standard's checkpoint (OWT-007–OWT-009) never blocks, because the condition it watches for is close to always true. See [the comparison table](#the-checkpoint-is-a-report-not-a-gate).
320
420
  - [class-level-fix](class-level-fix.md) — the general form of the wording-list limit OWT-011 discloses, and the source of the non-vacuous-evidence procedure OWT-015 requires.
321
421
  - [verification-evidence](verification-evidence.md) — the source of the exit-code and evidence-validity reasoning OWT-015 depends on; also where a partial-coverage exception (OWT-006, OWT-011) is registered rather than merely disclosed once.
422
+ - OWT-018 attaches to the same hand-back point as OWT-007: an edit to intent with no approver is one more thing listed there, and, like everything listed there, never blocks.
322
423
 
323
424
  - [deferred-item-exit](deferred-item-exit.md) — 同一個形狀的上游一半:DEX 要求延後項目離開文件、
324
425
  抵達可追蹤的出口,並刻意不規定出口的載體。本標準接手**出口存在之後**的事,
@@ -331,3 +432,5 @@ This standard's shape comes from one adopting project's observations made and ac
331
432
  - [verification-evidence](verification-evidence.md) — OWT-015 所依賴的 exit code
332
433
  與證據有效性推理的來源;也是 OWT-006/OWT-011 的部分涵蓋例外該被登記的地方,
333
434
  而不是揭露一次就放著。
435
+ - OWT-018 掛在與 OWT-007 相同的交回點:沒有核可者的意圖修改,是在那裡多列出來的一項,
436
+ 而且與列在那裡的一切相同,永不阻斷。
@@ -2,8 +2,8 @@
2
2
 
3
3
  > **Language**: English | [繁體中文](../locales/zh-TW/core/turn-completion-integrity.md)
4
4
 
5
- **Version**: 1.4.1
6
- **Last Updated**: 2026-09-28
5
+ **Version**: 1.5.0
6
+ **Last Updated**: 2026-09-29
7
7
  **Applicability**: Any harness where an agent ends a turn and hands control back to a human
8
8
  **Scope**: universal
9
9
  **Industry Standards**: none claimed — derived from observed failures, see Evidence
@@ -149,13 +149,14 @@ prevent, one level up.
149
149
  ## Supported harnesses
150
150
 
151
151
  The check is enforced only where a harness adapter exists and a hook is
152
- actually wired into that harness's own config. As of v1.4.1:
152
+ actually wired into that harness's own config. As of v1.5.0:
153
153
 
154
154
  | Harness | Event | Config file | Block contract |
155
155
  |---|---|---|---|
156
156
  | Claude Code | Stop | `.claude/settings.json` | stdout `{"decision":"block","reason":...}`, exit 0; silence allows |
157
157
  | Codex | Stop | `.codex/hooks.json` | stdout `{"decision":"block","reason":...}`, exit 0 — plain text or empty stdout is documented as invalid for this event |
158
158
  | Gemini CLI (legacy) | AfterAgent | `.gemini/settings.json` | stdout `{"decision":"deny","reason":...}`, exit 0 — the documented preferred path over exit code 2 |
159
+ | Antigravity CLI (`agy`) | Stop | `.agents/hooks.json` | stdout `{"decision":"continue","reason":...}`, exit 0; `{}` allows |
159
160
 
160
161
  Wired is not running on Codex. Codex skips a project hook until the project
161
162
  is trusted **and** that exact hook definition has been trusted through `/hooks`
@@ -184,14 +185,58 @@ turn may be missed.
184
185
  Gemini CLI is legacy. Google retired it for personal accounts on
185
186
  2026-06-18 in favour of Antigravity CLI (`agy`); enterprise accounts keep
186
187
  access to both. The adapter stays for those users, but it has never been
187
- confirmed against a real Gemini CLI session, and new adopters on Google's
188
- tooling should expect Antigravity CLI, which is **not yet supported**. Its
189
- documented Stop hook contract differs from every adapter above in the ways
190
- that matter: the hook is configured in `.agents/hooks.json`, the payload
191
- carries only a `transcriptPath` (no final message, no human message), and a
192
- block is `{"decision":"continue","reason":...}`, not `block` or `deny`. An
193
- adapter will be added once that contract has been observed against a real
194
- session — the same reason Cursor below has none.
188
+ confirmed against a real Gemini CLI session; new adopters on Google's tooling
189
+ should use the Antigravity CLI adapter below.
190
+
191
+ Antigravity CLI is supported, on a contract observed in a real session
192
+ (2026-09-29, agy 1.2.12) rather than taken from its documentation alone. The
193
+ contract differs from every adapter above in the ways that matter:
194
+
195
+ - **Config** is `.agents/hooks.json`, keyed by hook name —
196
+ `{"<name>": {"Stop": [{"type":"command","command":"...","timeout":N}]}}`,
197
+ `timeout` in seconds. There is no `hooks` wrapper and no `hooks[]` nesting
198
+ around the handler.
199
+ - **stdin carries neither the final reply nor the human's message**, only
200
+ `transcriptPath` and metadata, so both are read from the transcript (JSONL,
201
+ each record with `source`, `type`, `content`). The final reply is the last
202
+ record with `source: MODEL`, `type: PLANNER_RESPONSE`. The human's message is
203
+ the last record with `source: USER_EXPLICIT`, `type: USER_INPUT`, taken from
204
+ inside `<USER_REQUEST>…</USER_REQUEST>` — the system blocks that follow it
205
+ (`<ADDITIONAL_METADATA>` and others) are not the human's words.
206
+ - **`SYSTEM_MESSAGE` records MUST NOT be read as the human's message.** agy
207
+ writes this hook's own `continue` reason back into the transcript as one
208
+ (`source: SYSTEM`, `type: SYSTEM_MESSAGE`, "Stop hook blocked termination:
209
+ …"). Reading "any record that is not the model" as the human takes the hook's
210
+ own text for the human's words and voids the R9 exemption — the R11 failure
211
+ in this transcript's own shape.
212
+ - **A block is `{"decision":"continue","reason":...}`**, not `block` or `deny`;
213
+ `{}` allows.
214
+ - **The hook runs with `.agents/` as its working directory, not the project
215
+ root** (measured 2026-09-29, agy 1.2.12). The installed command is therefore
216
+ `node ../scripts/hooks/check-turn-completion-agy.mjs`; the project-root form
217
+ `node scripts/hooks/...` resolved to `<project>/.agents/scripts/hooks/...`,
218
+ failed with "Cannot find module", and **agy let the failed hook through
219
+ silently** — no message, stdout unchanged, the turn simply ended. The path is
220
+ relative on purpose (the file is meant to be committed and shared, and an
221
+ absolute path is one machine's) and uses no shell syntax (`sh -c`, `$(...)`),
222
+ because whether agy runs `command` through a shell has no evidence behind it.
223
+ - **Unlike Claude Code, the transcript already holds the final reply** when the
224
+ hook runs.
225
+
226
+ Verified: agy 1.2.12, non-interactive `agy -p`, a **single turn with no tool
227
+ calls** — the hook was called with the final reply already in the transcript,
228
+ `continue` was honoured (the model produced a further turn), and no trust
229
+ prompt was encountered (unlike Codex). **Not verified**: multi-turn
230
+ conversations, turns that include tool calls (whether the last
231
+ `PLANNER_RESPONSE` is then the final reply, and whether it is already written
232
+ when the hook runs), `fullyIdle: false`, a non-empty `error`, interactive mode,
233
+ whether a project `.agents/hooks.json` is only honoured for a registered
234
+ Antigravity project (as `.agents/skills/` is), and whether the working
235
+ directory is always `.agents/` (it was measured for a project-level file only;
236
+ a user-level `~/.gemini/config/hooks.json` is not written by `uds init`). In an
237
+ unverified case the adapter may judge an earlier reply rather than the final
238
+ one; any failure to read still allows (R5). `uds init --with-hooks` prints the
239
+ verified range next to the install line.
195
240
 
196
241
  Cursor was evaluated and is not supported: whether its stop hook can actually
197
242
  block a turn in the way this standard requires was unresolved as of this
@@ -257,3 +302,5 @@ only because a corpus existed; the two that shipped were the ones no case covere
257
302
  - [ ] The attribution search excludes the check's own headings and scaffolding
258
303
  - [ ] Each supported harness's block contract (config file, event, output shape) is verified against that harness's own docs, not assumed from another harness
259
304
  - [ ] The installer only writes a harness's hook config for a harness the adopter selected
305
+ - [ ] A harness whose transcript records system-written messages reads the human's message from the human's records only, never from "anything that is not the model"
306
+ - [ ] A harness contract is shipped only for the range observed against a real session, and the unobserved range is stated