Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"plugins": [
{
"name": "issue-driven-dev",
"version": "2.103.1",
"version": "2.103.2",
"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.",
"author": {
"name": "Che Cheng"
Expand Down
22 changes: 22 additions & 0 deletions plugins/issue-driven-dev/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
carried the same blind spot, and all three of that round's new mechanisms had zero test weight. Hence the three rules:
third-party surfacing of design shape, a positive control for every guard, and "I enumerated exhaustively" is not
evidence.
## [2.103.2] - 2026-08-14

### Fixed

- **`process-attachments.sh` could not see the attachments this plugin itself uploads (#284)** — `idd-issue` Step 4
uploads to an `attachments` release and writes a `github.com/{o}/{r}/releases/download/{tag}/{file}` URL into the
issue body; the URL detector had no pattern for that shape, so `idd-diagnose` Step 1.5 reported
「Issue #N has no attachments (empty manifest written)」 and proceeded without the evidence it was supposed to read.
The write side and the read side of one plugin disagreed about a URL shape, and the disagreement was **silent** — an
empty manifest is indistinguishable from an issue that genuinely has none. (Filed three times as #284/#285/#287;
consolidated here.) The URL character classes were also tightened to exclude whitespace: a bare URL in prose used to
swallow the rest of the line.

- **Non-ASCII attachment names collided silently, and `--clobber` overwrote the loser (#286)** — GitHub sanitizes
release asset filenames by **stripping non-ASCII characters**. Measured: `issue_10_tpa2026_年會議程.pdf` and
`issue_10_nstc_心理學門成果發表會議程.pdf` came back as `issue_10_tpa2026_.pdf` and `issue_10_nstc_.pdf` — the Chinese
segment gone entirely. That instance survived only because the two ASCII prefixes happened to differ. A description
written *entirely* in Chinese — the natural thing for this plugin's own users — sanitizes to the same name for
different files, and with `--clobber` the second upload silently replaces the first: two links in the issue body, one
file behind them, no error anywhere. Names are now ASCII-folded before upload, an all-folded description falls back to
an indexed name rather than an empty one, and a pre-existing asset of the same name causes a rename with a note
instead of a clobber.
## [2.103.1] - 2026-08-14

### Fixed
Expand Down
15 changes: 13 additions & 2 deletions plugins/issue-driven-dev/scripts/process-attachments.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,18 @@ resolve_repo() {

detect_urls() {
# Patterns: github.com/user-attachments/{files,assets}/, github.com/{owner}/{repo}/files/N/,
# (private-)user-images.githubusercontent.com/
# (private-)user-images.githubusercontent.com/, and RELEASE ASSETS (#284):
# github.com/{owner}/{repo}/releases/download/{tag}/{file}
#
# The release-asset form is not an exotic case — it is what THIS PLUGIN itself
# produces. `idd-issue` Step 4 uploads attachments to an `attachments` release
# and writes the release-download URL into the issue body; `idd-diagnose`
# Step 1.5 then called this function, matched nothing, and reported
# "Issue #N has no attachments (empty manifest written)". The write side and
# the read side of the same plugin disagreed about the URL shape, and the
# disagreement was silent: an empty manifest is indistinguishable from an issue
# that genuinely has no attachments, so a diagnosis proceeded without the
# evidence it was supposed to read.
#
# Fetch and filter are deliberately SPLIT (#186): a zero-attachment issue makes
# grep exit 1, and under `set -euo pipefail` a single fetch|filter pipeline dies
Expand All @@ -117,7 +128,7 @@ detect_urls() {
raw=$(gh issue view "$NUMBER" --repo "$REPO" --json body,comments) || return 2
content=$(printf '%s\n' "$raw" | jq -r '.body, .comments[].body') || return 2
printf '%s\n' "$content" \
| grep -oE 'https://(github\.com/(user-attachments/(files|assets)/[^)]+|[^/]+/[^/]+/files/[0-9]+/[^)]+)|(private-)?user-images\.githubusercontent\.com/[^)]+)' \
| grep -oE 'https://(github\.com/(user-attachments/(files|assets)/[^)[:space:]]+|[^/]+/[^/]+/files/[0-9]+/[^)[:space:]]+|[^/]+/[^/]+/releases/download/[^/)[:space:]]+/[^)[:space:]]+)|(private-)?user-images\.githubusercontent\.com/[^)[:space:]]+)' \
| sort -u || true
}

Expand Down
30 changes: 27 additions & 3 deletions plugins/issue-driven-dev/skills/idd-issue/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1020,13 +1020,37 @@ gh release view $ATTACHMENTS_RELEASE --repo $GITHUB_REPO 2>/dev/null || \
gh release create $ATTACHMENTS_RELEASE --repo $GITHUB_REPO \
--title "Attachments" --notes "Issue attachments and figures"

idx=1
# 對 Step 1 蒐集到的每個附件依序上傳(命名規則:issue_${NUMBER}_${DESC}.${ext})
#
# DESC 必須是 ASCII(#286)。GitHub 會 sanitize release asset 檔名,**非 ASCII 字元
# 直接被剝除**。實測上傳 `issue_10_tpa2026_年會議程.pdf` 與
# `issue_10_nstc_心理學門成果發表會議程.pdf`,讀回來是 `issue_10_tpa2026_.pdf` 與
# `issue_10_nstc_.pdf` —— 中文整段消失。那次沒出事只因為兩者的 ASCII 前綴剛好不同。
#
# 對中文語境的使用者,DESC 寫成純中文是很自然的,而那會讓兩個不同附件 sanitize 成
# 同一個名字;再配上 `--clobber`,**第二個會靜默覆蓋第一個**,issue body 兩條連結
# 指向同一份檔案,而且沒有任何錯誤訊息。
for f in "${ATTACHMENT_PATHS[@]}"; do
ext="${f##*.}"
desc=$(make_desc "$f") # 簡短描述 e.g. "snq_timeline" / "telegram_msg_8169455616_photo1"
desc=$(make_desc "$f") # 簡短描述 e.g. "snq_timeline"
# ASCII-fold:非 [A-Za-z0-9._-] 一律轉底線,再摺疊連續底線
desc=$(printf '%s' "$desc" | LC_ALL=C tr -c 'A-Za-z0-9._-' '_' | sed 's/__*/_/g; s/^_//; s/_$//')
# 全部被摺掉(DESC 原本純非 ASCII)→ 退回可辨識的序號,絕不產生會相撞的空名字
[ -z "$desc" ] && desc="attachment_$(printf '%03d' "$idx")"
upload_name="issue_${NUMBER}_${desc}.${ext}"
gh release upload $ATTACHMENTS_RELEASE "$f" \
--repo $GITHUB_REPO --clobber

# --clobber 只有在「確定要覆蓋自己剛上傳的同一份」時才安全。先檢查同名 asset 是否
# 已存在且不是本輪產生的 —— 存在就換名,不靜默蓋掉別人的附件。
if gh release view "$ATTACHMENTS_RELEASE" --repo "$GITHUB_REPO" \
--json assets --jq '.assets[].name' 2>/dev/null | grep -qxF "$upload_name"; then
upload_name="issue_${NUMBER}_${desc}_$(printf '%03d' "$idx").${ext}"
echo "note: asset name collision — uploading as $upload_name instead" >&2
fi

gh release upload "$ATTACHMENTS_RELEASE" "$f#$upload_name" \
--repo "$GITHUB_REPO"
idx=$((idx + 1))
done

# 圖片 URL 格式(private 和 public repo 都適用)
Expand Down
Loading