feat: actionability gate round 2 — prefix tier + deferral vocabulary, wired into all four consumers (#316) - #318
kiki830621 wants to merge 8 commits into
Conversation
Verify Report — PR #318Enginepai-ensemble 2.20.0 (canonical #207) — 4 IDD lenses + DA + Codex (gpt-5.6-sol), model: opus AggregateFAIL — 2 CRITICAL, 21 HIGH blocking; 28 MEDIUM / 11 LOW follow-up Frozen snapshot Scope coveragePR refs: #37 #84 #136 #292 #298 #306 #309 #310 #316 #317 — 8 已 CLOSED/MERGED(敘述脈絡)、#317 為獨立 follow-up(PR body 自述不在本次修復範圍) #316 — bug: #298 的修正只落在 idd-listRequirements coverage: 部分達成,但兩個 CRITICAL 使核心宣稱未成立。 CRITICAL(blocking,兩者互相獨立)C1. 三訊號 gate 從未接進四個 consumer — parking-lot / Blocking 仍被繞過lens: Codex 的首要發現,也是它給出「建議修改後再合併」的理由: 問題:
對照 spec( 觸發情境: 後果:parser 回 exit 0,consumer 照 tier routing 直接執行。fixture 自己描述的 建議:(1) 每個 consumer 讀 labels 與 body 的 Blocking section;(2) 一律呼叫 C2. 封閉值域與本 repo 真實語料不符:90 筆真實 diagnosis 有 38 筆域外,其中 22 筆是舊 parser 路由正確、新契約改成 hard abort 的正常 issuelens: 四位評審都只讀 diff,沒有人拿真實資料跑過這個 helper。我跑了。 方法: 結果:
真實值長這樣(逐字,非我編的): 這推翻了本 change 的因果診斷。design/proposal 認定「截斷是 bug」,但在真實語料上截斷 22 次是對的、1 次是錯的(#200 那一類才是錯的)。把截斷整個拿掉、改成「任何後綴文字=域外=停止」,是用一個 24%(22/90)的 false-stop 去換一個 10%(9/90)的 false-go —— 而且 false-stop 是硬停,比原本的靜默誤路由更容易讓整條 pipeline 停擺。design 明文否決的替代方案 (b)「只加寬 parser」,才是符合資料的那一個。 proposal 的 BREAKING 估計也是錯的: 而且流量端沒有任何強制力:新的 producer 規則只是 重現: . plugins/issue-driven-dev/scripts/lib/actionability.sh
gh issue view 264 --json comments --jq '[.comments[]|select(.body|test("^## Diagnosis";"m"))]|last|.body' \
| python3 -c 'import sys,json;print(json.load(sys.stdin))' >/tmp/b # 或直接傳 body
idd_parse_complexity "$(cat /tmp/b)"; echo $? # → 3, unparseable-complexity: Spectra(opt-out → 直接 propose)HIGH(blocking)
MEDIUM / LOW(follow-up 候選)MEDIUM ×28
LOW ×11
INFO ×7含 4 份獨立的 prompt-injection 掃描結果(皆 clean)。 Scope Check有 scope creep:M44 指出一個 routing-parser 變更同時改了 IC_R011 sister-sweep 的 label 慣例( Lens 統計
|
`### Complexity` 從沒宣告過封閉值域,也沒有 unparseable 契約 —— 而結構相同的姊妹欄位 `### Conflict Class` 兩者都有。於是 producer 寫出 `Simple when triggered` 不違反任何明文 規則,三個 consumer 各自發明互不相容的窄化:idd-list 靜默截成 `Simple`、idd-all 與 idd-implement 得到非法字串。2026-08-10 實測本 repo 的 22-issue backlog,11 個 diagnosed 裡 8 個 parked/deferred 被判成「Actionable now」,其中兩個帶著使用者親自下的 defer 裁決。 根因不是 parser 太窄,是**把會變的狀態存進不可變的 artifact**。Complexity 活在 append-only 的 Diagnosis comment,parked 卻會變(trigger 成立就該解除)。#136 的 comment 寫 bare `Spectra`、body 寫 `Spectra when triggered (parking lot)` —— 狀態改不動就自己漂到別處。 IDD 早就知道正確做法:`### Blocking` 正因為會變才放在 body 由 idd-update 維護。 - tier 值域收斂為封閉四值(可帶既有 ` via <來源>` 後綴),延期狀態遷至 parking-lot label - 三訊號 gate(域外值 / label / Blocking 區塊),放行需三者皆不成立 - 解析與判定抽成 scripts/lib/actionability.sh,四個 consumer 全部改為引用 + fail-loud guard —— 沒有 guard 的話,「找不到就自己想辦法」的 consumer 會把三方分歧原封帶回來 三個刻意的取捨: Strategy 的 `- [~]` **不進 gate**。它的既有 consumer 是 idd-close 的 checklist gate,語意是 per-item 的 close-time disposition;拿它回答 per-issue 的 routing 問題會撞壞既有語意。 migration **只貼 label、不改寫歷史**。回填既有 Diagnosis comment 會 modify-in-place 一個 append-only artifact;而且不必要 —— legacy 值走 unparseable 路徑得到的正是「not-actionable + surface 原值」,對那批 parked issue 就是正確結果。實測當前 open backlog 域外值 0 筆。 producer **不自動貼 label**。限定詞是 diagnose 當下的判斷,label 是人的裁決,兩者會分岔 (11 筆實測只有 5 筆一致)。由前者推導後者,等於取消「人可以事後 park 一個 tier 明確的 issue」這條路徑。 顯示層仍分兩組:reason 僅為 blocking 者維持 #84 既有的 Blocked 分組(標題/banner/footer 逐字不變),其餘進 Parked 組。gate 統一不等於顯示統一。 ic-r011 的 `blocker:infeasible` / `blocker:waiting` 一併退役 —— 0 個 issue 在用、無任何 consumer 讀,(b)/(c) 的區分保留在 audit 字串(那是說明性資訊,不是機械判定用)。 47/47 suites 綠(新增 actionability-gate 66 條,fixture 含 2026-08-10 事故快照, 斷言 9 筆中只有 #37 為 actionable)。 Refs #298, #310, #317
… 定形 (#316) 第 1 輪的封閉值域被 /idd-verify --pr 318 CRITICAL-2 證偽:90 筆真實 diagnosis 有 38 筆 落在值域外,其中只有 9 筆是延期修飾語,22 筆是「tier + 同行理由」的正常寫法。分界畫錯了 位置 —— 不在「後面有沒有字」,而在「那些字是否表達延期」。 /idd-reorganize #316 裁定:design D1/D3/D5、spec R1/R3/R7 redo;D2/D4/D6/D7/D8 still-valid。 /idd-diagnose #316 第 2 輪以完整 corpus(225 issue 中 159 筆有 Diagnosis)重新定規則: 剝裝飾 → 取 tier prefix → 對整個值掃延期語彙。158/158,0 false positive。 本 commit 只動 Spectra artifact(ingest 產物),不動 code: - design.md:三個決策重寫並留「前版為何被推翻」註記;新增「風險姿態:label 為主、 語彙為輔」——延期語彙是開放列舉,漏抓有 label 兜底、誤抓會硬停正常工作,所以取高精度 - spec.md:R1/R3/R7 重寫,延期偵測獨立成 requirement(7 → 8 條);reason 值域 4 → 5, 新增 complexity-deferral-marker —— 與 unparseable 分離,因為人的處置不同(前者確認 label、後者修 diagnosis) - proposal.md:BREAKING 敘述移除。「9 筆需 migration」是從有偏樣本推的;新規則對既有 全部語料都對,migration 為零 - tasks.md:保留 10 [x] + 2 [~],新增第 6–9 組共 10 個 task。7.1 明寫「只換 parser 不 呼叫 verdict 等於沒修」—— 第 1 輪 gate 完整實作、66 測試全綠、零 consumer 呼叫 Refs #298, #310
a2e7de0 to
dbadd38
Compare
…inate (#316) rebase onto main 3.0.0 後重看 pending task:main 於 2026-08-15 post-merge audit 為 idd-list 記下 `--json comments` 硬截最舊 100 則的問題,並明寫「不得只在其中一個 consumer 修」。 本 change 的三個 consumer snippet 有同一個洞 —— latest Diagnosis 正是 >100 則時被丟掉的 那則,helper 會誤回 complexity-missing。修法沿用 check-closed-without-summary.sh 的現成實作。 Refs #335
…gate wired into all four consumers Refs #316 (round-2 re-baseline of PR #318) Round 1 declared a closed value domain for `### Complexity` and shipped a three-signal gate that no consumer called; /idd-verify --pr 318 returned FAIL (CRITICAL-1 unwired, CRITICAL-2 domain refuted by the 159-diagnosis corpus: 66 real values wrongly refused). Helper (scripts/lib/actionability.sh) - tier = leading whole word among Simple|Plan|Spectra|SDD-warranted after decoration strip; rationale / parenthetical / ` via` suffix are legal - whole first line scanned for deferral vocabulary -> exit 5, stderr `deferral-marker: <raw>`, no stdout (tier prefix never routable) - reason vocabulary 4 -> 5 (`complexity-deferral-marker`, group = parked) - new idd_blocking_section (idd-update's `- (none)` placeholder is empty) - shared fence-aware section extractor (``` / ~~~; same-or-higher heading ends) - value-less flag -> named exit 2 (was an infinite loop) Consumers (idd-list / idd-all / idd-implement / idd-plan) - actually call idd_actionability_verdict with all three signals - paginate the Diagnosis fetch (gh issue view --json comments = oldest 100) - set -euo pipefail-safe conditional capture; exit 2 is FATAL, not withheld - idd-list: Parked group added; #84 blocked heading/banner/footer verbatim Tests (225 assertions) - fixture rows now verbatim Diagnosis comment values; corpus-sampled shapes >=3 each; #128 pinned as the designed prose-deferral miss - frozen 159-issue corpus regression: 149 / 9 / 1 / 0 - strict-mode integration test; consumer-wiring drift guard Docs: references/actionability-gate.md rewritten (risk posture, corpus table, canonical call shape); idd-diagnose producer declaration rewritten; CHANGELOG 3.1.0; version 3.0.0 -> 3.1.0.
Verify Report — PR #318 (round 2)Enginemanual fan-out (4 lens Agents + sequenced DA, model: opus, file-based output) + Codex (gpt-5.6-sol) — Codex leg FAILED (codex-call HTTP 429 AggregateFAIL — 6 blocking, 10 in-scope fixes, 5 follow-ups Round 1's two CRITICALs are genuinely repaired and every lens mutation-tested it (renaming the verdict call in each SKILL.md turns the drift guard red; removing the deferral scan / leaking the tier on exit 5 / dropping fence tracking turn 73 / 22 / 6 assertions red). The DA independently re-implemented the reference's prose rule in Python and got 159/159 on the frozen corpus, so the corpus is not a tautology. The 21 round-1 HIGH findings check out on the named paths. But round 2 repeated CRITICAL-2's failure shape on the third signal: Scope coveragePR refs: #316 Process Gaps
#316 — bug: #298 的修正只落在 idd-list — 另三個 Complexity consumer 未動,且 idd-list 自身 Step 5 與 Step 3.7 互相矛盾Requirements coverage: spec R1 / R3 / R5 / R7 FULLY; R2 / R4 / R6 / R8 PARTIALLY (details in rows 6, 5, 2, 13 below). Issue Expected 6/6 FULLY; Acceptance criteria 6/9 FULLY, 3 knowingly superseded by the round-2 re-baseline but not annotated in the issue body (row 22). Blocking
In-scope fix (same PR, non-blocking)
Follow-up (beyond #316)
Scope CheckNo scope creep. CHANGELOG 3.1.0, the version bumps, the marketplace description and the PR-body rewrite are in-scope round-2 accompaniments; the ic-r011 label convergence is an acceptance criterion of #316 (and is what row 4 finds half-done). External-writes block for #316 read successfully by all five reviewers; it contains no instruction-shaped text and its two IC_R011 exemption claims match the diff — with one honesty gap: the sister sweep covered files changed, not contracts affected (rows 2 and 4 are divergences this round created). Verified as sound (so round 3 need not re-check)
NextRound-3 fixes in this PR (rows 1–18), then re-verify with Codex once its usage limit resets. Follow-up Findings Filed (v2.72.0+ #148)Filed by default per IC_R011 §1.1 from the Follow-up (beyond #316) rows above:
Rows 1–18 are being addressed in this PR (round 3). Codex re-run pending its usage-limit reset. |
…nosed group, gate before egress Refs #316 (round-3 repairs for the /idd-verify --pr 318 round-2 FAIL: 6 blocking + 10 in-scope) Helper (scripts/lib/actionability.sh) - idd_blocking_section reads the section PER BULLET (any non-placeholder bullet is a blocker; continuation lines belong to the bullet above) and recognises a placeholder by its LEADING TOKEN (none / n/a / 無, optionally bulleted, decorated, parenthesised, then EOL / ')' / separator). Round 2 anchored the whole line and withheld 31 of 47 empty sections, including #316's own `- (none — 可動)`. - trailing CR stripped before either section reader judges a line (GitHub web textarea submits CRLF) - idd_actionability_group gains `undiagnosed` (complexity-missing alone) - new frozen fixture corpus-blocking.json: all 55 `### Blocking` sections in this repo's issue bodies, hand-reviewed (47 empty / 8 non-empty), 0 FP / 0 FN; two rival rules measured there and rejected (documented in the helper) Consumers - idd-implement: gate moved to Step 0.35, ahead of tree-lock, branch creation and the Implementation Plan comment; test pins the line order - idd-list: Needs-diagnosis group keeps `→ /idd-diagnose #N`; Parked is now exactly the --parked set; labels/body/comments taken from the Step 2 bulk fetch (paginate only at >= 100 comments); open-state guard; per-row fetch failure marks the row instead of aborting; REASONS reset; C0 stripped - all four: issue number digit-checked before the REST path; Diagnosis comments trusted only from OWNER/MEMBER/COLLABORATOR; jq/python3 in allowed-tools; table header VEXIT · CEXIT · TIER - idd-all: Layer V sub-issue scan and Phase 3b.1 fallback paginate Contract / producer - references/actionability-gate.md: signal-3 risk posture, undiagnosed group, corpus denominator (66/225 never diagnosed), scoped producer prohibition, hardened canonical snippet, "gate SHALL precede any egress or branch" - idd-diagnose: prohibition scoped to the issue under diagnosis - idd-issue + spec delta idd-ic-r011-checkpoint: blocker:* retired for parking-lot - rules/sdd-integration.md: parallel bare-token parse narrative removed Tests (285 assertions): blocking corpus 55/55; CRLF both readers; per-bullet cases incl. `- none of the reviewers replied yet`; row 905 really emits a `- [~]` Strategy item; drift guard pins verdict capture shape, REASONS branch, FATAL branch, digit check, author filter, allowed-tools, gate-before-branch and gate-before-egress order, no `=$(gh issue view … --json comments)` left. Spec: R6 three groups, R7 scoped, new R9 (blocking signal per bullet, frozen corpus); design round-3 decisions + open question (#336); tasks 10.x. Follow-ups filed from the verify: #336 #337 #338 #339.
Round 3 pushed —
|
| Row | Change |
|---|---|
| 1 (CRITICAL) | idd_blocking_section reads per bullet with a leading-token placeholder rule; frozen corpus-blocking.json (55 real sections, hand-reviewed) is 0 FP / 0 FN. #316's own - (none — 可動) now reads empty; - (none) followed by - 等 … reports the blocker. The two rejected candidate rules are recorded in the helper. |
| 2 (CRITICAL) | undiagnosed group: idd-list shows Needs diagnosis (N): with → /idd-diagnose #N; Parked is now exactly the --parked set. Spec R6 amended. |
| 3 (HIGH) | idd-implement gate moved to Step 0.35 (before tree-lock, git checkout -b, and the Implementation Plan comment); contract clause + line-order guard. |
| 4 (HIGH) | Producer prohibition scoped to the issue under diagnosis; blocker:* retired in idd-issue and, via a spec delta, in the live idd-ic-r011-checkpoint spec. |
| 5 (HIGH) | Trailing \r stripped in the shared extractor; CRLF fixtures for both readers. |
| 6 (HIGH) | Drift guard pins the verdict conditional-capture shape, the REASONS= branch and the FATAL … misuse branch in all four consumers. |
| 7–18 | idd-all Layer V scan + 3b.1 fallback paginate; idd-list reuses its bulk fetch, open-state guard, per-row fetch failure; sdd-integration parallel narrative removed; digit check + author filter in all four snippets and the reference; allowed-tools jq/python3; row 905 emits a real - [~]; signal-3 risk posture, corpus denominator and mention-class recorded; fixture rows #160/#136 verbatim; ≥3-per-shape counts real rows only; table headers; REASONS reset; C0 strip; 158/158 → 159/159; plugin.json description synced. |
Suite: actionability-gate 285/285; 53 suites green except plan-routing-consistency (#335, environment-dependent). Follow-ups filed: #336 #337 #338 #339; acceptance-criteria errata posted on #316.
Not yet re-verified: Codex is under its usage limit until ~2026-09-08 14:00 (+08:00). A round-3 /idd-verify --pr 318 should run with the cross-model leg present.
Verify Report — PR #318 (round 3)Enginemanual fan-out (4 lens Agents + sequenced DA, model: opus, file-based output) + Codex (gpt-5.6-sol) — Codex leg absent again (HTTP 429 AggregateFAIL — 6 blocking, 11 in-scope fixes, 5 follow-ups Round 2's six blocking rows were re-measured by every lens (live Scope coveragePR refs: #316 · Verified scope: #316 Process Gaps
#316 — bug: #298 的修正只落在 idd-list — 另三個 Complexity consumer 未動,且 idd-list 自身 Step 5 與 Step 3.7 互相矛盾Requirements coverage: spec R3 / R4 / R7 / R8 FULLY; R1 / R2 / R5 / R6 / R9 PARTIALLY (rows 2, 3, 5, 10, 11 below). tasks §10: 10.5 FULLY, 10.1–10.4 / 10.6 PARTIALLY. Blocking (round 4, this PR)
In-scope fix (same PR)
Follow-up (beyond round 4)
Scope CheckNo scope creep: Verified as soundRound-2 rows 2, 6, 7, 9, 10, 11, 12, 13, 14, 18 FULLY on re-measurement; DA's premise ruling (recorded verbatim in spirit)Round 3 is in the regex rabbit hole (three rounds, each adding a character class, each growing a new hole). Round 4 should fix correctness and honesty, not coverage: repair the regressions (rows 1, 3), close the one-line fail-opens (rows 4, 6), make the verdict observable (row 2), bring every claim down to the evidence (rows 5, 7, 8), and hand #336 a corrected acceptance bar. Neither "tune the regex again" nor "demote signal 3" — the latter would be a mid-verify scope change against #84, and with a live effect of n = 1 the decision can be made calmly in #336. NextRound-4 fixes in this PR (rows 1–15), then a fresh verify — with Codex once its limit resets. Follow-up Findings Filed (v2.72.0+ #148)
Rows 1–15 addressed in round 4 (this PR). |
…the verdict, claims down to evidence Refs #316 (round-4 repairs for the /idd-verify --pr 318 round-3 FAIL: 6 blocking + 11 in-scope) Regressions repaired - idd-implement Step 2.5 consumed gate variables across Bash calls and forbade re-running; now checks ${VEXIT:-} and re-runs the Step 0.35 block with the same helper (the shape idd-all 3b.1 already had) - idd-list state guard used the listing --state flag (--state all skipped every issue, Suggested-next vanished); now per-issue .state == "OPEN", and `skipped` rows keep the phase x PR matrix - placeholder rule was locale-dependent (multibyte separators in a bracket expression flipped both directions under LC_ALL=C; the suite itself failed 3 assertions); rewritten as alternations, suite also run under LC_ALL=C - an unclosed code fence swallowed every later section (live #290); unbalanced fences now disable fence tracking for that body Observability / hygiene - the gate block prints `gate #N: VEXIT=… TIER=… REASONS=… | …` in all four consumers and the canonical shape (a silent exit 0 was indistinguishable from actionable to the executing model) - C0/DEL scrubbed at the helper's outputs (round 3 only described it, and the described set omitted \r); idd_actionability_group "" is exit 2 - REASONS reset on the actionable path; routing table rows carry all three keys; idd-plan gains allowed-tools and drops the bare --json comments fetch; idd-all Layer-V sub-issue scan digit-checks + author-filters; _idd_section_first_line no longer pipes into head (SIGPIPE under pipefail) Claims brought down to the evidence - corpus-blocking.json rows carry the original body so the extractor is exercised; provenance: 54/55 agree with hand review, #1 an accepted FP, 54/55 CLOSED issues the gate never evaluates (signal 3's live effect on 2026-09-07 was one issue) - accepted misses stated as a rule in both directions; the two rejected placeholder regexes recorded with FP/FN counts; "parked group == --parked set" claim deleted from spec R6 and the reference; cluster-path coverage gap (first issue only, pre-existing) stated and tracked in #340 - spec delta: `respectively` residue, nested backticks and a mis-attributed @trace removed; design/proposal/tasks/CHANGELOG synced; #336 acceptance bar is the semantic truth, not the fixture Tests: 318 assertions (LC_ALL=C probes, unclosed-fence probes, scrub probes, extractor-through-corpus, verdict-printed / REASONS-reset / re-run-fallback / per-issue-state / skipped-rule / sub-issue-hygiene drift guards). Follow-ups: #340 filed; reader-semantics items handed to #336.
Round 4 pushed —
|
| Row | Change |
|---|---|
| 1 (CRITICAL) | idd-implement Step 2.5 checks ${VEXIT:-} and re-runs the Step 0.35 block with the same helper when the variables did not survive the Bash-call boundary; the prohibition now reads "no private regex", not "no re-run". |
| 2 (HIGH) | The gate block prints gate #N: VEXIT=… TIER=… REASONS=… | … in all four consumers and the canonical shape; drift guard pins it. |
| 3 (HIGH) | idd-list guards on per-issue .state == "OPEN"; skipped rows keep the phase × PR matrix; the listing-flag guard is refuted by test. |
| 4 (HIGH) | Placeholder rule rewritten with multibyte separators as alternations; the suite runs the probes under LC_ALL=C (and the whole suite passes under LC_ALL=C). |
| 5 (HIGH) | Claims restated everywhere: 54/55 agree with hand review, #1 accepted FP, 54/55 CLOSED, extractor covered via original bodies; #336's bar is the semantic truth (48/7); the two rejected regexes are recorded with FP/FN. |
| 6 (HIGH) | Unbalanced fences disable fence tracking for that body; #290's section is found; balanced fenced examples still skipped. |
| 7–15 | C0/DEL scrub moved into the helper's outputs (CR included); accepted misses stated as a rule in both directions; cluster gap stated in the contract → #340; "parked group == --parked set" deleted; spec delta cleaned (respectively, nested backticks, @trace); idd-plan allowed-tools + Step 1 fetch; idd-all sub-issue digit check + author filter; table rows three-keyed; REASONS reset; idd_actionability_group "" → exit 2; no head in the first-line reader; 159/159 wording; proposal Modified Capabilities / Impact. |
Suite: actionability-gate 318/318 (also under LC_ALL=C); 53 suites green except plan-routing-consistency (#335). Follow-ups: #340 (cluster-path gate); reader-semantics items commented on #336.
Not yet re-verified: Codex is under its usage limit until ≈ 2026-09-08 13:30 (+08:00). A round-4 /idd-verify --pr 318 should run with the cross-model leg present — three consecutive 5-of-6 runs is one too many.
Verify Report — PR #318 (round 4)Enginemanual fan-out (4 lens Agents + sequenced DA, model: opus, file-based output) + Codex (gpt-5.6-sol via pai AggregateFAIL — 9 blocking (1 CRITICAL / 2 HIGH / 6 MEDIUM), 12 in-scope fixes, 2 follow-ups Round 3's fifteen rows were re-measured by every lens (live Scope coveragePR refs: #316 · Verified scope: #316 Process Gaps
#316 — bug: #298 的修正只落在 idd-list — 另三個 Complexity consumer 未動,且 idd-list 自身 Step 5 與 Step 3.7 互相矛盾Requirements coverage: spec R1–R8 FULLY; R9 (The blocking signal is read per bullet against a frozen corpus, Round-3 rows 1–15, re-measured
Blocking (round 5, this PR)
In-scope fix (same PR)
Added by the cross-model leg (Codex, in-scope)
Follow-up
Refuted (no action)
Round 5 prescription (DA, adopted)Two of the three helper rule changes this round had no measured need. Applied to a worktree, the two reverts below are a 4-insertion / 7-deletion drop-in: suite goes 313/5, and the five failures are exactly the five round-4 assertions (
Gate for any future reader-rule change to Baseline measurements (all re-run this round, none taken from the green suite)
External writes (#315)
Cross-model leg (Codex gpt-5.6-sol, effort high, round-4 diff minus the fixture file) — corroboration mapCodex reviewed blind (no access to the five Claude findings files) and returned FAIL with 2 HIGH / 3 MEDIUM / 3 LOW. Every Codex finding lands on a Claude row or adds one; no Claude blocking row was contradicted.
Codex's round-4 claim table agrees with the Claude re-measurement above on all 13 claims (成立 / 部分成立 / 未完全成立 line up with real / half). On out-of-diff writes it found #340 / #336 / #316 consistent with the diff, flagged that a "Round 4 pushed" comment claiming all six items fully repaired is partly refuted by rows 1–3 and 23, and read the Follow-up Findings Filed (v2.72.0+ #148)
Rows 1–9 (blocking) and 10–19, 23–24 (in-scope) → round 5, this PR. Refuted with no action: regression R4-M3. Verified snapshot |
…er widenings, delimit the printed verdict (#316) Change: add-actionability-gate Tasks: 28/28 complete (3 skipped with reason) Refs #316 (round-5 repairs for the /idd-verify --pr 318 round-4 FAIL: 9 blocking + 12 in-scope) Reverted (no measured need; both opened the fail-open direction) - odd-count fence heuristic: a closed ``` block containing a ~~~ line, or a fenced example followed by one stray opener, exposed the fenced template as the real section (Plan when triggered routed as Simple). Its motivating body got the same gate verdict either way; CommonMark renders its section as code. Fences now follow CommonMark; the EOF-probe variant was measured and rejected. - `.` / `。` as placeholder terminators: `- None. Waiting on X` read as empty. - the five round-4 assertions that pinned both, replaced by fence shapes A/B/C and "a full stop is not a terminator". Repaired - the gate prints a machine line of closed-vocabulary fields, then the raw third-party text inside raw<<< ... >>>raw, every line indented (4 consumers + canonical shape) - idd-implement Step 2.5 repeats the Step 0.35 block verbatim instead of echoing - reader greps pin LC_ALL=C; two-locale comparison on one input - group misuse message scrubbed; scrub comment states what it does not do - corpus claims test-locked: semantic_empty per row, 48/7, disagreement {1}, 54 CLOSED, frozen section == extractor output - idd-list: group call guarded against exit 2, `skipped` in Step 5's closed enumeration, every error exit prints why, body read under its own guard - idd-all digit check before the first gh call; idd-plan Simple row 3 keys - comments drift guard: one pattern, any --json field position, self-tested - honesty: bullet-class "measured" restated as a design judgment; rejected regexes on one denominator; 31 of the 48; 11/14 dated; @trace kept and updated - change gate recorded: a reader-rule change must flip a measured row or revert
…the change gate as closed lists (#316) Change: add-actionability-gate Tasks: 28/28 complete (3 skipped with reason) Refs #316 (self-review before freezing the round-5 verify) Two contradictions round 5 introduced, caught before verify: - The change gate allowed only "flip a measured row" or "revert"; the same round's LC_ALL=C pin is neither, so the gate as written refused its own commit. Now a closed list of three forms: (a) flip a measured corpus row, named in the commit; (b) revert; (c) remove an execution-environment dependence a pinned test demonstrates, every corpus row unchanged. - "Fences SHALL be interpreted as CommonMark interprets them" was false. The line-based reader follows CommonMark's two visibility rules (fenced example is never a section; unclosed fence runs to the end) and departs in three measured ways: fence length not compared, indentation not checked (fail-open), a closer with trailing text accepted. Each is now listed with its direction and pinned as a DOCUMENTED DIVERGENCE; on the live snapshot (243 bodies, 164 Diagnosis comments) reader and CommonMark disagree on section presence zero times, so the gate keeps the behaviour.
這個 PR 做什麼(第 2 輪,2026-09-07)
讓四個 routing consumer(
idd-list/idd-all/idd-implement/idd-plan)共用同一份### Complexity抽取與 actionability 判定,並且真的呼叫它。追蹤 issue #316。根因(兩層)
### Complexity活在 append-only 的 Diagnosis comment,但 parked 會變。IDD 早就知道正確做法:
### Blocking正因為會變才放在 body 由idd-update維護。規則(第 2 輪,corpus 159/159)
### Complexity下第一個非空行(code fence 不是區段;同級或更高 heading 結束區段)Simple/Plan/Spectra/SDD-warranted之一開頭(整詞、最長匹配優先);開頭即 tier,其後的理由/括號/
via <來源>一律合法when triggered/parking lot/deferred/暫緩→ exit 5,stderrdeferral-marker: <原值>,stdout 不印 tier(合法的前綴正是不能路由的理由)移入 discussion list)complexity-unparseable(資料錯誤,要修)complexity-missing(先 diagnose)complexity-deferral-marker(合法狀態,不是要修的東西)風險姿態:
parking-lotlabel 是主訊號,語彙是高精度低召回的安全網 —— 漏抓退回 #298 前行為(label 兜底)、誤抓是對可動 issue 的 hard stop,所以語彙保守、加詞要有 corpus 零誤報證據。
只掃第一行:掃整段會在 corpus 上多 2 個誤報(#154「remove deferred caveat」、#137「
deferredenum」)。主要改動
scripts/lib/actionability.shidd_blocking_section(- (none)placeholder 算空);共用 fence-aware 區段抽取器;value-less flag → exit 2(原本無限迴圈)skills/{idd-list,idd-all,idd-implement,idd-plan}/SKILL.mdidd_actionability_verdict(三訊號齊);Diagnosis 改用gh api … --paginate(--json comments只回最舊 100 則);set -euo pipefail安全的條件式捕捉;exit 2 是 consumer 自己的 FATALskills/idd-list/SKILL.mdskills/idd-diagnose/SKILL.mdreferences/actionability-gate.mdscripts/tests/actionability-gate/CHANGELOG.md/plugin.json/marketplace.json誠實的殘餘
Plan。fixture 把它 pin 為 actionable,不偽造 marker;要擋它由人貼 label。快照斷言因此從「只有 feature: /idd-all (無參數) bulk-solve mode — AI 自動 sequence + bundle 所有 open issue #37」改為「feature: /idd-all (無參數) bulk-solve mode — AI 自動 sequence + bundle 所有 open issue #37 與 feat: auto-@mention when idd-issue/idd-comment poses open question to a person (diagnose excluded) #128」。
Spectra—— 那是截斷 regex 自己說的話;真值是**Spectra**(Layer 2 + Layer 3 if/when triggered):…,延期藏在括號裡(這反而是「掃整行」的證據)。plan-routing-consistency在有 gitignored.spectra/snapshots/的開發機上會失敗、乾淨 worktree 通過 —— bug: plan-routing-consistency 測試掃到 gitignored .spectra/ snapshot — 乾淨 checkout 綠、跑過 spectra 的本機必紅 #335,本 PR 不動。驗證
plan-routing-consistency(bug: plan-routing-consistency 測試掃到 gitignored .spectra/ snapshot — 乾淨 checkout 綠、跑過 spectra 的本機必紅 #335,pre-existing、與本 PR 無關)actionability-gate225/225,含 159 筆 corpus 回歸 159/159#37 #128(後者為記錄在案的 designed miss)Spectra
openspec/changes/add-actionability-gate/—— 22/22 tasks(10 第 1 輪、2 skipped、10 第 2 輪)。