Skip to content

Commit c2dd479

Browse files
authored
fix: post-merge audit —— 三個「不會執行」的修法、一個會毀資料的腳本、兩個不會失敗的測試 (#319)
8d0ec33..737dbe0 跑 ensemble 稽核(#295 的 R7 從未被審過,#306#314 的 21 張 issue 完全沒有 ensemble)。4 CRITICAL + 17 HIGH。未被審的那半有一個一致的模式: **我把文件當成了實作**,而三個「已修好」的東西根本不會執行 —— 它們都通過了我的測試, 因為那些測試驗的是我寫的那塊,從沒驗它與 gh/檔案系統/讀者相接的那道縫。 CRITICAL - migrate-idd-config.sh 會搬走掃描範圍外的檔案並回報 ✓ migrated(find -print 配 read -r,含換行的路徑被拆成兩筆,第二筆是相對路徑、對呼叫端 CWD 解析)。改 -print0 + 拒絕掃描根外的路徑 + prune archive/worktrees + breadcrumb 不覆蓋既有檔。 - 取得層的修法從來沒有運作:gh api --paginate --jq 每頁各吐一個 array,--argjson 收到非法 JSON → 整個稽核在任何含長 issue 的 repo 上靜默停擺。改 jq -s add,另加 「補回來變少就拒絕」與 .number 的整數驗型。 - 同一個修法從未落到 /idd-list --audit-closes —— 那才是實際印出 --retroactive 邀請的 介面。已補上。 - bare_re 的尾錨把帶尾綴的強調標題送進 missing;新增 emph_re,錨點保留(它是散文 提及不會誤救的原因)。 修法無效者 - #286:gh release upload FILE#TEXT 的 # 是 display label 不是檔名,且更早之前 upload_name 就是 dead code —— 命名慣例從未被套用。改為 stage 成目標 basename 再上傳, 並還原 --clobber(拿掉它會讓合法重傳失敗)。 - #293/#305:七個呼叫點的 .[0] 一行沒改,只加了散文警告。精篩與時序檢查已進 code。 - #302:global 層沒有任何 reader,而我寫的理由(「已在 walk-up 路線上,只需多認一個 檔名」)描述的是一個沒做的改動。reader 已補,其餘 consumer 誠實記為 residue。 不會失敗的測試 - prose-drift 掃描大小寫敏感,而 marker 是大寫;且 positive control 種小寫 —— 控制項 通過而檢查是瞎的。已改為大小寫不敏感 + canary 用 canonical 大寫。 - 新增 acquisition-truncation suite:用 PATH 上的 gh stub 讓 live-gh 分支真的被執行 (既有 suite 全被 --json-file 短路,所以整段 19 行刪掉仍 46/46 綠)。acid 5/5 各自 轉紅;達到這個狀態改了三次斷言,三次都記在檔案裡。 其餘:idd-repo-map 補 sanitiser(它從零重造了隔壁腳本花七輪關掉的 row-forging)、 兩處 callout 被插進 bash fence 內部、marketplace.json 落後五個版本。 47/47 suites 綠(新增 acquisition-truncation 12 條)。
1 parent 737dbe0 commit c2dd479

18 files changed

Lines changed: 435 additions & 30 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"plugins": [
1616
{
1717
"name": "issue-driven-dev",
18-
"version": "2.103.3",
18+
"version": "2.109.0",
1919
"description": "v2.102.2: Deep Research light integration (#277, ruling b). idd-diagnose gains a non-binding pointer (the #111 superpowers hand-off shape: pure suggestion, no presence check, no dependency) fired when the diagnosis's quality depends on facts OUTSIDE the repo — with trigger examples AND counter-examples (the overly-broad-signal risk). Output flows back via '/idd-comment --type note' as summary + link, never full text (#116) — that is what keeps external research inside the audit trail. Both real-user misconceptions get canonical answers where they lived: research attaches AT diagnose (not after plan), and research vs implement are different phases' work, not substitutes. usecase-routing scenario 32 + a three-row internal-corpus vs external-world boundary table (idd-find / idd-ask / Deep Research). Deep integration stays a recorded residue until a plugin-dependable primitive exists. v2.102.1: reopen / resume path (#278) — the legal return trip from closed. idd-close gains a 'Reopen / resume path' section (close's dual operation): reopen-vs-new-issue criteria (same Expected -> reopen for trail continuity; morphed need -> new issue Refs old; broken upstream artifact -> #200's re-baseline, out of this path), resume point decided by the closing summary's WHY (premise changed -> re-diagnose; pure deferral -> implement), and the old summary stays untouched (append-only; reopen = note comment + idd-update phase rollback + optional prepend-note). Cross-referenced against auto-close-trap recovery. usecase-routing scenario 31. From a real user exchange; verify on substitute basis (disclosed). v2.102.0: three-front release. Skill-description contract + Path Map (#276, two-phase): idd-plan's frontmatter description — the ONLY surface read at skill-selection time — now names its diagnosis precondition; 5 skills gain the house pattern (drift-guard skill-description-contract, RED 8 first); docs/workflows.md gains a mermaid Path Flowchart mirroring the decision tree with all 36 catalog paths, rendered deterministically to the wiki Path-Map page by scripts/generate-path-map.py (drift-guard path-map-sync: freshness / coverage / discovery). Egress data-safety cluster (#275 + #273): empty-body guard — a provided-but-empty body now refuses (exit 15, band discipline; edit floors at 10 stripped chars because overwrite semantics turn empty dispatch into data loss — live incident 2026-07-22; explicit-intent escape --allow-empty-body); and the comment-PATCH surgery channel enters the nets via the new edit-comment verb (the #226 rollout's tracked-separately whitelist debt retired — it had bypassed EVERY net), with idd-edit's batch loop consuming the refusal band into a second outcome bucket (final exit stays 4). Dogfood: the #163 contract layer caught this release's own SCRUB_LEVEL provenance gap on first sweep. 42 suites, 0 fail. v2.99.1: staleness sweep + guard-net expansion (#267). README carried three stale gpt-5.5 pins and a stale vendored-codex-call claim — all outside the drift-guard scan net; fixed and the net widened: model-generation-sync now refutes pins in README + both catalog docs (31 assertions), and a new docs-catalog-sync suite requires every skills/* directory to appear in the catalog docs (the #122 no-forcing-function root cause is now test-detectable; it caught idd-ask and idd-config on its first RED). docs/workflows.md + skill-dimensions.md backfilled to v2.99 reality (P-find-lookup / P-ask-history / P-report-rollup / P-config-maintain / P-verify-file-profile paths, matrix rows, D12 4th member). 38 suites 0 fail. v2.99.0: /idd-ask — grounded QA over the issue corpus (#72), the surfacing family's 4th member mirroring /spectra-ask. Natural-language question -> decide-to-search gate (greetings/meta skip; bug-shaped questions never trigger diagnose) -> retrieval delegating idd-find's search backend (family rule: never rebuild a read-only query) -> full-text read of top-N hits (default 5, capped 10) -> grounded synthesis: first line blockquotes the question, every claim carries an issue/comment citation, source priority closed-with-PR > open > orphaned comment with conflicts surfaced, ending with Referenced Issues; corpus silence reported honestly, never filled from training memory. Read-only allowed-tools locked. First live run of the #140 fourth-member procedure (Q3 weak-hit judgment recorded in the family canonical). New capability spec idd-ask (+2 requirements); new drift-guard suite; 37 suites 0 fail. v2.98.0: codex channel goes full-dependency (#264, user ruling 'like superpowers'). The vendored bin/codex-call is DELETED — it trailed pai 2.18.0 by four security/correctness fixes (token-exp NSNumber parse, OAuth-file umask 0o077, form-encoding escape, post-flock re-read). Executable now resolves from the parallel-ai-agents plugin cache (MIN_PAI 2.19.0 — the codexModel/codexEffort contract floor, pai issue 22); model/effort/max-time governance resolves from codex-pro's EXTERNAL-CONSUMER CONTRACT (MIN_CODEX_PRO 0.7.0: machine-readable references/defaults.json base + global/project profile.yaml overlay, codex-pro issue 7) and is passed explicitly on all three call paths (canonical Workflow args + manual fan-out + legacy direct). IDD's tree contains ZERO model pins — generation bumps touch codex-pro's defaults.json only. Dependency wiring mirrors the superpowers shape: install-time dependencies entry (codex-pro@codex-pro), allowCrossMarketplaceDependenciesOn, check-plugin-presence pre-flight, fail-fast with a one-step install instruction, no soft fallback. model-generation-sync drift-guard reshaped to the v2 contract (a re-vendored codex-call fails the suite). 36 suites 0 fail. v2.97.0: 9-issue drain via 5 cluster PRs (#259-#263). Composable verification profiles (#258): idd-verify --profile code|prose|academic (+ config-registered custom via verify_profiles) switches the (lens set, DA focus, input source, freshness) four-tuple; new --file/--dir input sources make the git worktree optional; file-mode SHA-256 freshness gate mirrors the #228 diff gate (never silently exempted); code default byte-identical. New /idd-find skill (#139): surfacing-only semantic lookup over the open+closed corpus with GitHub relevance + phase/PR overlay; read-only, filter flags redirect to idd-list, embedding honestly deferred. Dashboard comment contract (#133) + idd-report --rollup (#134): one human-facing narrative snapshot per issue (marker-located, updates bound to phase transitions only, anti-#116) and a pull-only four-group attention view (need-attention / in-progress / stalled>14d / recently-closed). sdd_bias config switch (#252): hard-gate hits escalate to Spectra when high; default routing byte-identical. Layer V unattended deferred-record (#120): registry literal + structured catch-up record aggregated by idd-all Phase 6. Surfacing-primitives family doc, D12 axis (#140). Model-generation sync (#251): codex-call default gpt-5.6-sol is the tree's single generation pin (live-probed); prose generation-neutral; idd-route candidate renamed codex-xhigh. Docs path catalog completed (#122). 5 new drift-guard suites; 36 suites 0 fail. v2.96.0: gh-egress hardening cluster + idd-edit batch semantics. Exit-code band >=10 (#227: 10=privacy/11=mention/12=unscannable/13=attestation/14=usage; wrapper never exits <10 on its own — rc<10 is always gh's, so unattended callers can split gate-refusal from gh-failure on $? alone). Unified python3 content-net scan (#225: kills the jq/no-jq divergence; taxonomy = projects keys + path-shaped values under sensitive key names; fail-closed wide net when python3 absent). Phase 2 rollout (#226: all 6 skills' comment/edit egress now dispatch through gh-egress with attestation — the #117 mention net is mechanically enforced on the comment channel). idd-edit batch x R5 (#158: per-comment refuse + continue, batch outcome report, exit 4 iff any refused). v2.95.0: Discussions intake bridge (#221) — opt-in `idd-list --discussions` (GraphQL surface: Q&A/Ideas + unanswered + deduped vs issue refs; graceful no-op) + `idd-issue --from-discussion` (Provenance seed + draft-and-confirm reply, unattended never posts); cardinal rule: never auto-file. Plus idd-verify diff-freshness gate (#228: FROZEN_SHA vs HEAD before aggregate — refuse stale-snapshot verdicts) and the IDD_CALLER registry (#161: dynamic tree-sweep drift-guard). v2.94.0: selective git auto-tag (#85) — idd-issue tags idd-{N}-baseline at main HEAD (rollback anchor); idd-verify tags idd-{N}-verified on Aggregate PASS (review snapshot). Only these two milestones (no diagnose/plan/implement tags) so the tag namespace stays clean. Config `auto_tag` (default-ON, opt-out via enabled:false); idempotent (existing tag skipped) + graceful-skip on push failure (never aborts the workflow). v2.93.1: collaborator identity registry in idd-config (#86) — optional `collaborators[]` config field mapping a person's alias / email / display-name → GitHub @login WITHOUT guessing (github_login required; email is PII, private/gitignored only). tagging-collaborators.md Step 2.5 consults the registry first as an accelerator (a hit is still existence-verified via `gh api users/<login>`; a miss falls through to the API fuzzy-match); idd-config validate checks login charset + globally-unique aliases + PII reminder. v2.93.0: reshape Plan / pre-implementation tier (Cluster C, #129/#57/#111, via reshape-plan-preimpl-tier Spectra change) — first-class `meeting` issue type (meeting-first routing + Phase A/B/C deliberation + self-contained close gate), complexity hard gate (>=5-file interdependent-concept OR shared-abstraction MUST-trigger Plan, escalate-only), and superpowers pre-implementation hand-off (README stage-mapping table + non-binding brainstorming pointer, no self-built staging skill). v2.92.1: hotfix — parallel-ai-agents install-time dependency pointed at the wrong marketplace (psychquant-claude-plugins), making v2.92.0 fail to load and silently dropping all /idd-* skills; corrected to the parallel-ai-agents marketplace. v2.92.0: /idd-all batch-drain release — 23 issues verified+closed via 16 PRs (#223, #229-#243), the plugin's largest self-dogfood. Added: unattended-contract (state-file signal + TTL, TTY heuristic removed, idd-all/chain dependency early gates #123/#222/#211); gh-egress unconditional @-mention net with --mention-attested escape-or-attest contract (#117) atop 6-item mechanical-net precision hardening (#203); idd-close Step 6.3 doc-sync sweep (#220); test aggregator + GitHub Actions CI, 21 suites (#217); idd-list blocked-state grouping + all-blocked banner (#84); config Mechanism 3.5 submodule routing (#162); check-plugin-presence enabled-state detection exit 3 (#212); monorepo host plugin disambiguation (#68); assert-helpers eval-content ban + safe output-grep pair (#188); diagnosis-detection contract fixtures (#61). Changed: parallel-ai-agents promoted to install-time dependency, vendored ensemble fork DELETED, idd-verify two-tier chain (#219); DA sequenced-spawn eliminates the #119 socket-crash polling window (#130); spectra-archive-post-ic --force-linked-issue vs --linked-issue intent separation (#172); worktree conventions unified on the managed helper (#169); bridge state migrated to .claude/.idd/state/bridge.json (#199); .gitattributes LF policy (#216); merge-completeness fixtures default-branch self-sufficiency (#224). Audits: dependency bindings vs deep-integration rule (#210), rules layering 12/12 (#215). Follow-ups filed: #225-#228.",
2020
"author": {
2121
"name": "Che Cheng"

plugins/issue-driven-dev/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "issue-driven-dev",
33
"description": "v2.102.2: Deep Research light integration (#277, ruling b). idd-diagnose gains a non-binding pointer (the #111 superpowers hand-off shape: pure suggestion, no presence check, no dependency) fired when the diagnosis's quality depends on facts OUTSIDE the repo — with trigger examples AND counter-examples (the overly-broad-signal risk). Output flows back via '/idd-comment --type note' as summary + link, never full text (#116) — that is what keeps external research inside the audit trail. Both real-user misconceptions get canonical answers where they lived: research attaches AT diagnose (not after plan), and research vs implement are different phases' work, not substitutes. usecase-routing scenario 32 + a three-row internal-corpus vs external-world boundary table (idd-find / idd-ask / Deep Research). Deep integration stays a recorded residue until a plugin-dependable primitive exists.",
4-
"version": "2.108.0",
4+
"version": "2.109.0",
55
"author": {
66
"name": "Che Cheng"
77
},

plugins/issue-driven-dev/CHANGELOG.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,77 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.109.0] - 2026-08-15
9+
10+
### Fixed — post-merge audit of the 2026-08-13/14 session
11+
12+
The `#295` line reached `main` at round 7 without any independent review, and the twenty-one issues in PRs #306#314
13+
had none at all. An ensemble audit of `8d0ec33..737dbe0` returned 4 CRITICAL and 17 HIGH. The pattern it found in the
14+
unreviewed half is worth stating plainly: **documentation was shipped and recorded as implementation.** Three "fixes"
15+
did not execute. Each of them passed the author's tests, because those tests exercised the code that was written and
16+
never the seam where it meets `gh`, the filesystem, or a reader.
17+
18+
- **`migrate-idd-config.sh` moved files outside the tree it was told to scan, and reported success**`find -print`
19+
with `while read -r` splits a path containing a NEWLINE into two entries, and the second fragment is a **relative**
20+
path that `dirname`/`mv` then resolve against the caller's cwd. Reproduced: with a victim at
21+
`caller/.claude/.claude/…` and a newline-named repo inside the scan root, `--apply ../scan` relocated the victim —
22+
entirely outside the scan — and printed `✓ migrated: .claude`. Now `-print0`/`read -d ''`, plus a guard that refuses
23+
any path not under the scan root, `archive/` and `.claude/worktrees/` pruning, and a breadcrumb that will not truncate
24+
an existing file. This is the first script here that moves user data; it was moving the wrong files.
25+
26+
- **The round-7 truncation repair never worked, and never reached the surface users invoke**`gh api --paginate --jq`
27+
emits **one JSON array per page**, so `--argjson` rejected the concatenation, jq died, and the empty-payload guard
28+
silently disabled the entire audit on any repo containing a >100-comment issue (verified against
29+
`microsoft/vscode#301011`). Folded with `jq -s add`, plus a refusal to swap in a re-fetch that *shrinks* the comment
30+
set and integer validation on `.number` before it reaches an API path. Separately, the repair had only ever been
31+
applied to `scripts/check-closed-without-summary.sh`; **`/idd-list --audit-closes` — the surface that actually prints
32+
the `--retroactive` invitation — still had the truncated fetch**, and now documents the same repair.
33+
34+
- **`bare_re`'s trailing anchor sent emphasised headings to `missing`**`**Closing Summary** - fixed the parser` has a
35+
tail, so the phrase-only form rejected it and no hash form matched. `emph_re` covers it; the anchor stays, because it
36+
is what keeps ordinary prose out of the presence test (5 of 9 genuinely-missing issues in a real repo mention the
37+
phrase in prose and must stay flagged).
38+
39+
- **`#286` was inert**`gh release upload FILE#TEXT` sets a **display label, not the asset name**; the asset always
40+
takes the on-disk basename. An earlier revision computed `upload_name` and never used it at all, so the documented
41+
naming convention had **never** been applied. Attachments are now staged under the target basename before upload, and
42+
`--clobber` is restored (removing it broke legitimate re-uploads).
43+
44+
- **`#293`/`#305` was documentation only** — a contract file plus a prose ⚠ near each site, while all seven call sites
45+
still ran `.[0]` on a coarse search. The client-side filter and the `createdAt` ordering check are now in the code at
46+
the gate, the branch resolution and verify's auto-detect, and the reference examples are correct rather than merely
47+
annotated.
48+
49+
- **`#302`'s global layer had no reader** — the claim that the path was "already on the walk-up route, just recognise
50+
one more filename" described a change that had not been made; the walk-up only ever checked `local.json` and the
51+
legacy name. The reader exists now, as a last resort that announces itself; the remaining consumers are recorded as
52+
residue rather than implied to be done.
53+
54+
- **`idd-repo-map.sh` reproduced the row-forging channel from scratch** — a `github_repo` containing a newline emitted a
55+
standalone forged row and corrupted the footer counts; ESC reached the terminal raw; tabs shifted columns. The sibling
56+
script spent seven rounds closing exactly this. The shape of that script was copied without its safety; both now
57+
sanitise every field that reaches stdout.
58+
59+
- **Two prose callouts had been inserted inside fenced bash blocks** (`pr-flow.md`, `idd-close`), breaking the very
60+
commands the contract says get copied.
61+
62+
- **`marketplace.json` was five releases behind `plugin.json`** (2.103.3 vs 2.108.0) — the version-conflict resolution
63+
had forced the maximum onto only one of the two files.
64+
65+
### Fixed — tests that could not fail
66+
67+
- **The prose-drift scan was case-sensitive against a canonically-capitalised marker**, so it could not fire on the
68+
realistic literal — **and its positive control planted the lowercase form**, so the control passed while the check was
69+
blind. A positive control that certifies a capability the check does not have is worse than no control at all. The
70+
scan is case-insensitive and the canary now plants the canonical case.
71+
72+
- **New suite `acquisition-truncation`** — the truncation repair lives in the live-`gh` branch, which every existing
73+
suite skips because `--json-file` short-circuits it; the audit deleted all nineteen lines with 46/46 still green. The
74+
new suite stubs `gh` on PATH so the real code runs, and acid confirms **5/5** of its mechanisms turn assertions red on
75+
their own. Reaching that took three attempts, each recorded in the file: the first assertion could not tell "recovered"
76+
from "failed safely", the second could not tell either from "the audit aborted before printing anything" (hence a
77+
canary), and the shrink guard needed its own stub.
78+
879
## [2.108.0] - 2026-08-14
980

1081
### Added

plugins/issue-driven-dev/references/config-protocol.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ User runs `/idd-issue`, attaches label `cross-package`. Re-resolve picks the gro
751751

752752
**位置的四個理由**(考慮過 `~/.idd/`,不採用):
753753

754-
1. walk-up **已經**會經過 `$HOME/.claude/.idd/` —— 終止條件的檢查在 break 之前,所以這條路徑本來就在讀取路徑上,只需要多認一個檔名,零新增掃描邏輯
754+
1. walk-up 的終止條件檢查在 break 之前,所以 `$HOME/.claude/.idd/` **目錄層級**上就在讀取路徑上 —— **檔名不是自動就認的**。這一點原本寫成「只需要多認一個檔名」,而那個改動當時並沒有做,於是 global 層有了規格卻沒有任何 reader(post-merge audit 2026-08-15 指出)。現已在 `scripts/check-closed-without-summary.sh` 的 walk-up 之後補上讀取(repo-local 皆未命中時才用,並印一行說明來源)。**其餘 consumer 尚未接上 —— 那是 residue,不是已完成的事**
755755
2. 與 project 層的 `.claude/.idd/local.json` 完全對稱,只差 `local` / `global`
756756
3. IDD 是 Claude Code plugin,`~/.claude/` 是它的生態家(`settings.json``rules/``plugins/` 都在此);另開 `~/.idd/` 等於在 home 再放一個 dotdir。
757757
4. 檔名**必須**`global.json` 而非 `local.json` —— 後者會讓 `$HOME` 被誤讀成「一個 repo」,汙染既有的 repo-boundary 判定。

0 commit comments

Comments
 (0)