From c3b904522ea8aba80b70a4b8a2b395bd727883e7 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 12:40:30 +0800 Subject: [PATCH 1/6] fix: sync .agents/skills/spectra-archive/SKILL.md from .claude/ (#93) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigation during #93 implementation found that `.agents/skills/` is LIVE (referenced by 4 openspec specs as Spectra-tier dependencies — not legacy as the diagnosis recommended). Original diagnosis recommended deletion; revised disposition (audit comment on #93) is SYNC. This commit syncs only the `spectra-archive` skill — the original #93 scope. `.claude/skills/spectra-archive/SKILL.md` (14865 bytes) was the canonical post-#56 version with Implementation Complete auto-post feature + Step 0 Bootstrap discipline. `.agents/skills/spectra-archive/SKILL.md` (5694 bytes) was pre-#56 — missing both features. Copied .claude/ over .agents/ so the Spectra CLI / agents.md-format callers also get the #56 feature. OUT OF SCOPE for this commit: - `plugins/issue-driven-dev/references/spectra-skills/spectra-archive/` — no markdown cross-refs found; left as historical snapshot per feedback_lead_minimal (low cleanup ROI, no observable callers). - Sister divergences for 7 other spectra-* skills (spectra-audit spectra-discuss spectra-propose spectra-apply spectra-ingest spectra-debug spectra-commit) — beyond #93 stated scope. Audit comment on #93 documents the sister-skill drift matrix; each candidate filed for separate follow-up as needed. - Drift-prevention CI check — deferred until drift recurs naturally. Refs #93 --- .agents/skills/spectra-archive/SKILL.md | 121 +++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/.agents/skills/spectra-archive/SKILL.md b/.agents/skills/spectra-archive/SKILL.md index 7a269df..444b4d7 100644 --- a/.agents/skills/spectra-archive/SKILL.md +++ b/.agents/skills/spectra-archive/SKILL.md @@ -11,10 +11,27 @@ metadata: Archive a completed change. -**Input**: Optionally specify a change name after `$spectra-archive` (e.g., `$spectra-archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. +**Input**: Optionally specify a change name after `/spectra-archive` (e.g., `/spectra-archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. **Prerequisites**: This skill requires the `spectra` CLI. If any `spectra` command fails with "command not found" or similar, report the error and STOP. +**Step 0: Bootstrap Stage Task List** (required) + +Before doing anything else, call `TaskCreate` to build a harness-level todo list for this archive run — one entry per step below. Mark each `TaskUpdate → completed` as you finish it; silent completion is a violation. This mirrors the Step 0 Bootstrap discipline every `idd-*` skill enforces, and gives per-step accountability when `/idd-close` cascades into `/spectra-archive`. + +``` +TaskCreate(name="prompt_change_name", description="Step 1: resolve change name — prompt via AskUserQuestion if not provided / inferable") +TaskCreate(name="check_artifacts", description="Step 2: spectra status --json — warn + confirm if any artifact not done") +TaskCreate(name="check_tasks", description="Step 3: scan tasks.md — warn + confirm if incomplete - [ ] tasks") +TaskCreate(name="assess_delta_sync", description="Step 4: compare delta specs vs main specs; prompt sync now / archive without sync") +TaskCreate(name="cleanup_tracking", description="Step 5: rm -f .spectra/touched/.json") +TaskCreate(name="run_archive_cli", description="Step 6: spectra archive ") +TaskCreate(name="post_implementation_complete", description="Step 7: invoke spectra-archive-post-ic.sh to post ## Implementation Complete to the linked GitHub issue (#56)") +TaskCreate(name="display_summary", description="Step 8: read Step 7 outcome + show archive completion summary") +``` + +Complete each step → `TaskUpdate → completed` immediately. + **Steps** 1. **If no change name provided, prompt for selection** @@ -93,13 +110,111 @@ Archive a completed change. **If archive fails** with "already exists" error, suggest renaming existing archive. -7. **Display summary** +7. **Post `## Implementation Complete` to linked GitHub issue (v1.3+, PsychQuant/issue-driven-development#56)** + + **Purpose**: ensures `/idd-close` Step 0 supersession gate triggers for Spectra-path issues, removing the need for manual retroactive Implementation Complete synthesis. + + **Delegated to executable helper script** `.claude/scripts/spectra-archive-post-ic.sh` (with unit tests at `.claude/scripts/tests/spectra-archive-post-ic/`). The script is the source of truth — this skill calls it and reads the outcome. Behavior contract (detection / idempotent guard / safe body composition / multi-candidate handling) lives in the script + its tests, not in skill prose. This separation was introduced after R2 verify found that prose-with-illustrative-bash had structural defects (variable persistence across Bash tool calls, Python3 RCE via shell-string interpolation, etc.) — see PsychQuant/issue-driven-development#56 R2 verify report. + + **Required inputs from caller (agent)**: before invoking Step 7, the agent MUST have these in scope (from earlier skill steps): + - `$CHANGE_NAME` — the Spectra change name (slug; same value passed to `spectra archive`). **This exact value must also be reused, byte-identical, in Step 8** — the outcome file path is derived from it, so any drift (typo / whitespace / case) makes Step 8 read the wrong path. + - `$SPEC_DELTAS` (optional) — comma-separated capability names from `spectra archive` stdout in Step 6 (e.g., `"idd-all-chain, idd-spawn-manifest"`); defaults to placeholder if absent + + **Invocation**: + + ```bash + # Resolve repo root to make the script path cwd-independent — the skill may be + # invoked from a subdirectory (cd openspec && /spectra-archive ...). Relative + # path .claude/scripts/... breaks; absolute path via git-root prefix doesn't. + REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + + ARCHIVE_DIR="${REPO_ROOT}/openspec/changes/archive/$(date +%Y-%m-%d)-${CHANGE_NAME}" + SPEC_DELTAS="${SPEC_DELTAS:-(see archived change directory)}" + + # The helper script DERIVES the outcome file path internally from + # --change-name: /tmp/spectra-archive-ic-outcome-.txt + # (--change-name is allowlist-validated inside the script before being used + # in the path, so the derived path is always traversal-safe). The formula + # has a single source of truth — the script — so this skill does NOT pass + # --outcome-file. Step 8 recomputes the same path from the same $CHANGE_NAME + # to read the outcome across separate Bash tool calls. + # Why deterministic, not $$-$(date +%s): a random suffix cannot be recomputed + # in a second shell, which breaks the Step 7 → Step 8 handoff (R4 verify + # R4-S1 finding). The change name is the skill's primary input — always known + # to Step 8 — so the derived path IS recoverable. + bash "${REPO_ROOT}/.claude/scripts/spectra-archive-post-ic.sh" \ + --change-name "$CHANGE_NAME" \ + --archive-dir "$ARCHIVE_DIR" \ + --spec-deltas "$SPEC_DELTAS" + POST_IC_EXIT=$? + ``` + + **Exit codes**: + + | Exit | Meaning | Agent action | + |------|---------|--------------| + | `0` | Success or normal skip (posted / none / idempotent / unsafe-name / generic failure) | Read outcome from the derived outcome file (see Step 8), proceed to Step 8 | + | `2` | Usage error (missing args, or unsafe `--outcome-file` path) | Skill bug — fix invocation | + | `64` | Dependency missing (python3) | Surface to user; archive itself succeeded; manual retry after install | + | `75` | Multi-candidate detected | **AskUserQuestion required** — read `/tmp/spectra-archive-candidates.txt` for the candidate list, prompt user to pick canonical issue (show `#N + gh issue title` for each), then re-invoke script with `--linked-issue ` | + + **Stdout**: a single line that is either the IC comment URL, or one of the documented status messages (`(none — ...)`, `(skipped — ...)`, `(pending — ...)`, `(failed — ...)`). The same line is also written to the derived outcome file `/tmp/spectra-archive-ic-outcome-${CHANGE_NAME}.txt` for cross-Bash-call persistence (Step 8 reads from there). + + **Multi-candidate flow (agent responsibility)**: + + ```bash + if [ "$POST_IC_EXIT" = "75" ]; then + # Read candidates + AskUserQuestion + re-invoke (script re-derives the outcome path from --change-name) + CANDIDATES=$(cat /tmp/spectra-archive-candidates.txt) + # For each candidate, fetch title via `gh issue view --json title -q .title` + # Then AskUserQuestion: "Multi-candidate detected: which is canonical?" + # User picks → CHOSEN_ISSUE= + bash "${REPO_ROOT}/.claude/scripts/spectra-archive-post-ic.sh" \ + --change-name "$CHANGE_NAME" \ + --archive-dir "$ARCHIVE_DIR" \ + --spec-deltas "$SPEC_DELTAS" \ + --linked-issue "$CHOSEN_ISSUE" + fi + ``` + + The agent (LLM-driven) handles the AskUserQuestion step — bash cannot prompt. The script validates `$CHOSEN_ISSUE` against the original candidate set on re-invoke. + + **Failure semantics**: any failure in Step 7 (gh auth lost, network, body too large, etc.) is recorded in the outcome file but does NOT abort the overall archive operation — the archive itself (Step 6) has already succeeded, and the archived change directory + main spec deltas are the canonical record. The GitHub comment is the convenience anchor for `/idd-close` supersession. + + **Testing**: run `.claude/scripts/tests/spectra-archive-post-ic/test.sh` to validate the script against fixture archive directories (covers explicit-marker / Refs-fallback / no-marker / multi-candidate / malicious-tasks.md / missing-tasks.md / unsafe-change-name / linked-issue-resolved / linked-issue-invalid / outcome-path-derivation / unsafe-outcome-file). All 11 fixtures pass. + +8. **Display summary** + + Read the outcome from Step 7. Recompute the outcome file path the same way the + script derived it — from the **identical** `$CHANGE_NAME` slug passed to + `spectra archive` in Step 6. This works whether Step 7 + Step 8 ran in the same + Bash invocation (var still in scope) OR in separate Bash calls (the formula is + deterministic). **The `$CHANGE_NAME` here MUST be byte-identical to Step 7's — + no typo, trailing whitespace, or case difference** — otherwise Step 8 reads a + different path: + + ```bash + # Recompute the same path the script derived in Step 7. + # MUST use the identical $CHANGE_NAME slug — see contract note in Step 7. + OUTCOME_FILE="/tmp/spectra-archive-ic-outcome-${CHANGE_NAME}.txt" + + if [ -f "$OUTCOME_FILE" ]; then + IMPLEMENTATION_COMPLETE_POSTED=$(cat "$OUTCOME_FILE") + else + # Loud failure — NOT a quiet "(unknown)". A missing outcome file means + # either Step 7 never ran, or $CHANGE_NAME drifted between Step 7 and + # Step 8. Both are real errors the user must see. + IMPLEMENTATION_COMPLETE_POSTED="⚠️ ERROR — outcome file not found: $OUTCOME_FILE (Step 7 did not run, or \$CHANGE_NAME drifted between Step 7 and Step 8)" + echo "WARNING: spectra-archive Step 8 — outcome file missing: $OUTCOME_FILE" >&2 + fi + ``` Show archive completion summary including: - Change name - Schema that was used - Archive location - Spec sync status (synced / sync skipped / no delta specs) + - **Implementation Complete posted to:** `$IMPLEMENTATION_COMPLETE_POSTED` - Note about any warnings (incomplete artifacts/tasks) **Output On Success** @@ -172,3 +287,5 @@ Target archive directory already exists. - If sync is requested, use the Skill tool to invoke `spectra-sync-specs` (agent-driven) - If delta specs exist, always run the sync assessment and show the combined summary before prompting - If **AskUserQuestion tool** is not available, ask the same questions as plain text and wait for the user's response +- **Step 8 multi-candidate disambiguation**: if linked-issue detection (Fallback 1 explicit marker) returns multiple distinct `#N` values, MUST prompt user via AskUserQuestion to pick the canonical one — never auto-pick to avoid posting to the wrong issue +- **Step 8 silent skip on no linked issue**: do not warn or prompt; not all archives have GitHub trackers (legacy archives, design-only changes). Reflect skip reason in Step 7 summary line From 3517c1263002cad40f392a395f4ad57df3c4b8c8 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 12:42:12 +0800 Subject: [PATCH 2/6] fix(idd-implement, pr-flow): cluster detection glob hardening + Option A-final doc (#100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #100 Finding 1 (design) — Option A confirmed final: NEW `### Feature-branch + cluster + direct-commit — rejected case` subsection in `references/pr-flow.md` § Cluster mode override. Documents the rejected Option B (branch-context-gated cluster direct-commit) with comparison table + rationale. Contract simplicity wins; users who want feature-branch direct-commit workflow can use single-issue `--no-pr` invocations or cherry-pick post-merge. #100 Finding 2 (refactor) — glob hardening: `skills/idd-implement/SKILL.md` Step 0.5 cluster detection bash. Previous glob `\#[0-9]*` over-matched: - `#42abc` → matched (`*` absorbed trailing letters) — counted malformed - `#34 #34` → counted as 2 (duplicate) — tripped CLUSTER_MODE on 1 distinct Replaced with strict integer check + associative-array dedup: case "$arg" in \#*) arg_num="${arg#\#}" if [[ "$arg_num" =~ ^[0-9]+$ ]] && [ -z "${SEEN_ISSUES[$arg_num]:-}" ]; then SEEN_ISSUES[$arg_num]=1 ISSUE_COUNT=$((ISSUE_COUNT + 1)) fi ;; esac Matches batch-and-cluster.md documented `^#\d+$` form. 0 behavior change for well-formed distinct invocations. Malformed tokens (`#42abc`) silently skipped — counter doesn't trip on garbage. Duplicates collapse to 1. Refs #100 --- .../issue-driven-dev/references/pr-flow.md | 17 ++++++++++++++++ .../skills/idd-implement/SKILL.md | 20 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/issue-driven-dev/references/pr-flow.md b/plugins/issue-driven-dev/references/pr-flow.md index c93f9d0..e84a3ce 100644 --- a/plugins/issue-driven-dev/references/pr-flow.md +++ b/plugins/issue-driven-dev/references/pr-flow.md @@ -65,6 +65,23 @@ Then proceeds as PR path. **No abort, no silent ignore** — the flag is acknowl **Single-issue invocation behavior is unchanged** — the cluster carve-out only fires on ≥2 `#N`. Backward compatibility preserved. +#### Feature-branch + cluster + direct-commit — rejected case (v2.70.0+, #100 Finding 1) + +PR #99 (#96 implementation, Option A) chose to unconditionally force PR for cluster mode, regardless of the starting branch. Devil's Advocate during verify flagged that the "force PR" rationale (stacked half-isolated changes on default branch) **only holds when the user starts from the default branch**. On a non-default feature branch, cluster direct-commit just stacks N `Refs #N` commits on that feature branch — a legitimate workflow (one local feature tracking N issues, shipped as one PR later). + +The alternative was **Option B** (branch-context-gated cluster direct-commit): if current branch != default branch, honor `--no-pr` / `pr_policy=never`; otherwise force PR as today. This issue confirms **Option A is final**: + +| Aspect | Option A (current) | Option B (rejected) | +|--------|--------------------|--------------------| +| Contract simplicity | Cluster → PR. Uniform regardless of branch context. | Cluster → PR if default branch, else direct-commit. Two paths. | +| Override notice | One wording, mirrors fork detection | Two wordings depending on branch context | +| `git symbolic-ref` dependency | None | Required (detached HEAD / merge-state edge cases) | +| Cluster-on-feature-branch frequency | Rare (most cluster invocations are explicit `--pr`) | Same rare frequency, but now requires branch-context check overhead | + +**Recommendation**: keep Option A. The feature-branch direct-commit workflow remains viable for **single-issue** invocations (which honor `--no-pr` / `pr_policy=never`). Users who want cluster-on-feature-branch direct-commit pattern can: (a) run cluster as PR + cherry-pick or rebase to feature branch post-merge, or (b) run N atomic single-issue `--no-pr` invocations on the feature branch. + +If cluster-on-feature-branch direct-commit becomes a common pattern (not anticipated based on current usage), revisit Option B in a future issue. Until then, contract simplicity wins. + Cross-reference: full cluster semantics in [batch-and-cluster.md](batch-and-cluster.md). ### `pr_policy` config field diff --git a/plugins/issue-driven-dev/skills/idd-implement/SKILL.md b/plugins/issue-driven-dev/skills/idd-implement/SKILL.md index 5d1d64f..3377b34 100644 --- a/plugins/issue-driven-dev/skills/idd-implement/SKILL.md +++ b/plugins/issue-driven-dev/skills/idd-implement/SKILL.md @@ -110,14 +110,30 @@ TaskCreate(name="sister_bug_sweep", description="Step 5.7: review session log + ``` ```bash -# 1. Parse flag + count issue args (cluster mode = ≥2 #N) +# 1. Parse flag + count DISTINCT well-formed issue args (cluster mode = ≥2 #N) +# +# v2.70.0+ #100 Finding 2 — glob hardening: +# - Previous glob `\#[0-9]*` matched any `#` including +# malformed tokens like `#42abc` (the `*` absorbed the trailing letters) +# - Duplicate args like `#34 #34` over-counted as 2, tripping CLUSTER_MODE +# on a single distinct issue +# - Strict regex `^#[0-9]+$` (matching batch-and-cluster.md documented form) +# + associative-array dedup closes both issues PR_FLAG="" # "pr" / "no-pr" / "" +declare -A SEEN_ISSUES=() ISSUE_COUNT=0 for arg in "$@"; do case "$arg" in --pr) PR_FLAG="pr" ;; --no-pr) PR_FLAG="no-pr" ;; - \#[0-9]*) ISSUE_COUNT=$((ISSUE_COUNT + 1)) ;; + \#*) + # Strict integer check + dedup (v2.70.0+ #100) + arg_num="${arg#\#}" + if [[ "$arg_num" =~ ^[0-9]+$ ]] && [ -z "${SEEN_ISSUES[$arg_num]:-}" ]; then + SEEN_ISSUES[$arg_num]=1 + ISSUE_COUNT=$((ISSUE_COUNT + 1)) + fi + ;; esac done CLUSTER_MODE="false" From 0351d57d3f05259629f55a49af41288f385be346 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 12:44:06 +0800 Subject: [PATCH 3/6] fix(idd-issue): pasted-image immediate-persistence in Step 1 (#112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #112 surfaced that Claude Code's ~/.claude/image-cache// is per-session + cleared by context compaction / session lifecycle / session-id rollover. Step 1 → Step 4 separation (read annotation in Step 1, upload in Step 4) spans AskUserQuestion + Step 2.5/2.6 + Step 3 `gh issue create` + Step 4 upload — easily long enough for cache eviction. Step 4's `gh release upload ` fails when `ls` returns nothing. 2026-05-20 downstream incident (kiki830621/ai_martech_global_scripts#788) hit exactly this failure mode. Two changes to skills/idd-issue/SKILL.md Step 1 Source Type Adapter: 1. NEW "Pasted image" row in Source Type Adapter table — explicit immediate-persistence rule (cp in the same tool turn that reads the annotation, not deferred). 2. Updated "Mixed (text + 圖片貼上)" row — explicitly references the pasted-image immediate-persistence rule for every pasted image; the pre-existing "使用者另外提供 path 清單" is preserved as the non-pasted-image case (paths that already point at stable files outside the per-session image cache). 3. NEW `### Pasted-image immediate-persistence (v2.70.0+, #112)` subsection between Source Type Adapter and MCP pre-flight. Documents: - The cache-eviction failure mode + why immediate persist closes it - Bash snippet showing the cp loop + staged-path tracking - Step 4 reference contract (iterate PASTED_IMAGES_STAGED, not original cache paths) - Staging path naming convention (/tmp/idd-issue-attachments/issue_pending__.png) + rationale (POSIX-safe, anonymous, system-cleanup-friendly, no repo pollution) - Compaction-resumed session edge case (fallback to warn + continue when source already evicted before Step 1) 0 backward compat impact for non-pasted-image sources (docx via export_image, file paths in stable locations) — change is purely additive for the [Image: source: ...] annotation path. Refs #112 --- .../skills/idd-issue/SKILL.md | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md index b7c8152..f977148 100644 --- a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md +++ b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md @@ -273,8 +273,48 @@ UPSTREAM=$(echo "$REPO_JSON" | jq -r '.parent.nameWithOwner // empty') | Telegram chat range | `mcp__plugin_che-telegram-mcp_telegram-all__get_chat_history(chat_id, limit)` 或 `dump_chat_to_markdown` | 列舉 chat 中所有 `[photo]` / `[document]` / `[video]` placeholder → 嘗試 MCP `download_file`(若存在)→ 否則**明列檔名 + 必要請求**讓使用者用 Telegram client 手動存檔到指定路徑後 skill 接手 upload | | Apple Mail / 郵件 | `mcp__plugin_che-apple-mail-mcp_mail__get_email(message_id)` | `list_attachments` → `save_attachment(filename, output_path)` | | Apple Notes | `mcp__plugin_che-apple-notes-mcp_notes__get_note` | 同上 export 全部 inline 圖 | +| Pasted image (`[Image: source: ~/.claude/image-cache/...]`) | n/a — image-only | **立即** `cp` 到 `/tmp/idd-issue-attachments/issue_pending__.png` 在 *讀到 annotation 的同一 tool turn* — see "Pasted-image immediate-persistence" below (v2.70.0+, #112) | | 直接貼文字(無附件) | argument 直接帶文字 | n/a | -| 混合(文字 + 圖片貼上) | argument 帶文字 + 使用者另外提供 path 清單 | 把使用者給的 path 全部納入 Step 4 上傳清單 | +| 混合(文字 + 圖片貼上) | argument 帶文字 + `[Image:...]` annotation | **每張 pasted image 都套用 Pasted-image immediate-persistence**;使用者額外提供的 file path 直接納入 Step 4 上傳清單 | + +#### Pasted-image immediate-persistence (v2.70.0+, #112) + +**Why this step**: Claude Code's `~/.claude/image-cache//` is per-session + cleared by context compaction / session lifecycle / session-id rollover (continued session under fresh id). Step 1 → Step 4 separation (read annotation in Step 1, upload in Step 4) spans `AskUserQuestion` + Step 2.5/2.6 + Step 3 `gh issue create` + Step 4 upload — easily long enough for the cache to be evicted between turns. When this hits, Step 4's `gh release upload ` fails because `ls` returns nothing; the user has to re-paste, violating the *spirit* of Step 1's data-preservation hard rule. + +**Rule (SHALL)**: when Step 1 encounters a `[Image: source: ]` annotation in the prompt, **`cp` the image to a stable staging path within the SAME tool turn** that first sees the annotation. Do NOT defer to Step 4. The staged path joins Step 4's upload list; the original `~/.claude/image-cache/` path is no longer referenced after Step 1. + +```bash +# Run in the SAME tool turn that first sees the [Image: source: ...] annotation. +# Do NOT split into a separate turn — that's the bug class #112 surfaced. +mkdir -p /tmp/idd-issue-attachments + +# Track staged paths in an array for Step 4 to upload. +PASTED_IMAGES_STAGED=() +for src_path in "${PASTED_IMAGE_PATHS[@]}"; do + if [ ! -f "$src_path" ]; then + # Cache already evicted before Step 1 ran (rare — happens in compaction-resumed sessions + # where the new agent loop runs under a fresh session id, original cache dir is gone). + # Fallback: ask user to re-provide. Documented in spec as the known failure mode. + echo "⚠ Pasted-image source $src_path no longer exists (cache evicted)." >&2 + echo " Please re-paste the image OR provide a stable path; continuing without this attachment." >&2 + continue + fi + # Stage to /tmp with timestamp + random suffix (POSIX-safe, anonymous, no repo pollution). + # System /tmp housekeeping cleans these eventually; no manual cleanup required. + staged_path="/tmp/idd-issue-attachments/issue_pending_$(date +%s)_$RANDOM.png" + cp "$src_path" "$staged_path" + PASTED_IMAGES_STAGED+=("$staged_path") + echo "→ Staged $src_path → $staged_path" +done +``` + +**Step 4 reference contract**: when uploading attachments, iterate `PASTED_IMAGES_STAGED[@]` (NOT the original `[Image: source:...]` paths). The annotation in the prompt is only the *initial pointer*; the staged copy is the durable artifact. + +**Why `/tmp` not in-repo `.claude/.idd/issue-pending/`**: anonymous + system-cleanup-friendly + doesn't pollute version control. The in-repo alternative was considered but rejected per `feedback_lead_minimal` — system housekeeping handles cleanup without policy surface. + +**Why not Read-then-Write via Claude Code tools**: Bash `cp` preserves bytes exactly + handles binary efficiently. Read/Write would re-encode through Claude's text channel for binary content. + +**Compaction-resumed session edge case**: if the cache was evicted before Step 1 even runs (extreme: long pre-Step-1 turn or session-id rollover), the `[ ! -f "$src_path" ]` check fails — fallback prints a warning + continues without that attachment. User can re-paste in a follow-up. #### MCP plugin presence pre-flight (v2.54+, #27 fail-fast) From 9623ec104b7bbda2e4d249a2f26b998e632f9ca9 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 12:44:41 +0800 Subject: [PATCH 4/6] =?UTF-8?q?chore(idd):=20bump=20v2.70.0=20=E2=80=94=20?= =?UTF-8?q?chain-5=20cluster=20(#93=20#100=20#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #93 #100 #112 --- .../.claude-plugin/plugin.json | 2 +- plugins/issue-driven-dev/CHANGELOG.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/issue-driven-dev/.claude-plugin/plugin.json b/plugins/issue-driven-dev/.claude-plugin/plugin.json index a814a9e..32f95f0 100644 --- a/plugins/issue-driven-dev/.claude-plugin/plugin.json +++ b/plugins/issue-driven-dev/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "issue-driven-dev", "description": "v2.63.0: #96-backlog Simple cluster — 6 docs/reference follow-ups shipped via cluster-PR #101 (PsychQuant/issue-driven-development #60 #62 #63 #78 #90 #91). These are the Simple-tier subset of an 18-issue `/idd-diagnose` batch (6 Simple / 12 Plan) run over the #96-backlog cleanup; the 12 Plan-tier issues are driven separately. #60: NEW `## Cluster-PR eligibility (when to bundle vs split)` section in `references/batch-and-cluster.md` — a criteria table (same-file ✓ / same-skill ✓ / same-root-issue chain-only ✓ / same-label ✗ / same-review-timing ✗) plus a borderline >50-line review-surface heuristic, with a cross-reference from `idd-implement/SKILL.md`'s Cluster-PR mode paragraph; motivated by PR #58 having bundled unrelated issues #49+#53 under only a shared parent label. #62: `references/usecase-routing.md` decision-tree section gains a bulk-solve note pointing to row 27 — there is no built-in zero-arg backlog bulk-solve; use per-issue `/idd-all` or `/idd-all-chain`. #63: `usecase-routing.md` row 27 `#44 chain-solve` plain text upgraded to an explicit `[#44 chain-solve](url)` link for raw-markdown-viewer cross-link consistency with the already-linked `#37`/`#46`. #78: `idd-issue/SKILL.md` multi-finding override-flags section gains a ⚠ CI-caller note — automated / CI / `/loop` callers expecting the pre-v2.55.0 always-single-issue behavior of `idd-issue source.docx` MUST pass `--no-multi-finding` explicitly (v2.55.0 changed the default to auto-enter multi-finding mode on ≥2 findings); a retroactive behavioral-change notice was added to `CHANGELOG.md` (placed under `[Unreleased]` then rolled into this entry — no standalone `## [2.55.0]` entry exists). #90: NEW `openspec/CONVENTIONS.md` documenting the `**GitHub-side tracker**: #NN` canonical Spectra-proposal → GitHub-issue linking convention (collapses `idd-close`'s 3-fallback detection chain to a one-line lookup). R1 placed this at `openspec/LANGUAGE.md`; the 6-AI cluster verify's Devil's Advocate caught (coordinator-confirmed) that `openspec/LANGUAGE.md` is a reserved filename — `spectra-discuss` reads it as the project's canonical vocabulary file with a vocabulary-drift capture mechanism — so R2 relocated the convention to `openspec/CONVENTIONS.md` (purpose-built, verified not reserved by any skill). #91: `.claude/skills/spectra-archive/SKILL.md` gains a `Step 0: Bootstrap Stage Task List` section before its `**Steps**` block, with 8 `TaskCreate` entries mapping 1:1 to the skill's existing Steps 1-8, matching the idd-* Bootstrap discipline; the parallel tool-managed command-file surface (`.claude/commands/spectra/archive.md`, wrapped in `` regenerated markers) was intentionally NOT hand-edited — its Step-0 gap is folded into #93's 4-copy divergence scope. Cluster-PR #101 verified by 6-AI cluster verify: R1 CONDITIONAL PASS — 4/5 Claude reviewers PASS, Devil's Advocate surfaced 2 HIGH blocking findings (#90 reserved-name collision, #91 invocation-surface scope), both coordinator-confirmed via file-existence checks; R2 PASS after #90 relocation + #91 re-scoping. Codex (6th reviewer) hung and never returned — recorded as an explicit process gap rather than hidden in the aggregate. Squash-merged to main as `0eb419c`. v2.62.0: cluster mode override — pr-flow.md canonical documentation + idd-implement Step 0.5 bash implementation (PsychQuant/issue-driven-development#96). Resolves a 3-file contradiction in IDD's PR-vs-direct-commit path resolution: `pr-flow.md` canonical resolution-algorithm table had no cluster carve-out while `idd-implement/SKILL.md:49` + `batch-and-cluster.md:133` independently asserted cluster-PR mode forces PR (\"不接受 --no-pr\"); the three files contradicted and the behavior on `--no-pr` + cluster collision (abort / warn / silent ignore) was never specified. Surfaced during PR #94's cluster work. Option A (user-selected in `/idd-all` session from 3 diagnosis candidates A/B/C): maintain forced PR for cluster mode, but make it explicit + consistent. NEW `pr-flow.md` `### Cluster mode override` subsection — cluster mode (any IDD skill invoked with ≥2 `#N` args) is a multi-issue mode where all cluster issues share one feature branch + one PR; path resolution is `idd-implement`'s job (the only skill that resolves PR-vs-direct-commit) and for it cluster mode is a precondition that pre-empts the resolution-algorithm table and forces PR path; `idd-verify` / `idd-close` are cluster-aware but operate on the cluster's already-existing PR — they consume the path decision, they don't make it. Explicit override notice mirrors fork detection (`→ cluster mode (N issues) → PR path enforced (overriding --no-pr / pr_policy=never)`); fork+cluster co-occurrence prints both notices (the two pre-emptions independently force PR path, no precedence question). `idd-implement` Step 0.5 bash wired with cluster detection: parse `#N` token count in `$@` → derive `CLUSTER_MODE` → pre-empt block before the existing flag/fork/policy resolution → `OVERRIDE_SRC` accumulation composes the actual triggering condition(s) into the notice; Step 0.5 local algorithm summary gains a row 0 noting cluster pre-emption. `batch-and-cluster.md:133` rule statement demoted to a pointer at the new canonical section with the rationale phrase (\"stacked half-isolated changes on default branch\") inlined verbatim in `pr-flow.md` for downstream-grep stability. Verified 6-AI × 2 rounds: R1 (doc-only `cbe6f5d`) CONDITIONAL PASS — 5/6 reviewers converged on a HIGH doc/code gap (spec described a Phase 0.5 override notice the bash had no capability to emit); R2 (`5351116`) extended PR scope per user opt-in to add the ~24-line bash impl (8-case behavioral dry-run + `bash -n` clean), 6/6 PASS with Devil's Advocate explicitly recommending MERGE; R3 (`04c51cb`) closed DA's one new actionable finding (the subsection originally over-claimed cluster mode \"pre-empts the Resolution algorithm\" for verify/close — those skills never run path resolution). Step 0.8 auto-close-trap scan clean. Backward compat: single-issue invocation (`idd-implement #19`) byte-equivalent — the cluster carve-out only fires on ≥2 `#N`. Follow-up #100 tracks 2 non-blocking deferred items (Option A still forces PR on a non-default feature branch where cluster direct-commit is a legitimate workflow — Option B revisit candidate; cluster-detection glob `\\#[0-9]*` over-counts malformed/duplicate tokens vs the stricter documented `^#\\d+$`). PR #99 squashed as `b7f72ff`. v2.61.0: idd-verify Step 0.8 — squash-commit-body auto-close trap fix (PsychQuant/issue-driven-development#97). Step 0.8 (added in v2.60.1 by PR #94) extended from a 1-source scan (PR body via `closingIssuesReferences`) to a 2-source scan covering: (1) PR body authoritative parse via `closingIssuesReferences` (kept), and (2) per-commit `messageHeadline` + `messageBody` via `gh pr view --json commits` + trap regex `(^|[^-/[:alnum:]])(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]]*:?[[:space:]]+#[0-9]+` (case-insensitive via `tolower($0)`). R1/R2/R3 lessons baked into the regex from PR #94's verify history: `(^|[^-/[:alnum:]])` prefix excludes `/idd-close #N` IDD skill invocations and hyphenated tokens; `:?` covers the colon form; `[[:space:]]+` mirrors GitHub's space requirement. Same-repo `#N` form only; cross-repo `owner/repo#N` deferred per Plan D7. The fix addresses the ironic v2.60.1 dogfood failure where PR #94 itself was squash-merged and GitHub auto-closed `#87` two seconds later because one of PR #94's commits had a body that *quoted* the trap pattern as a verify-finding example. R2 (in-PR fix after R1 verify) extended the jq filter from body-only to headline+body after Devil's Advocate + Codex independently confirmed the missed-subject channel with empirical evidence: commit `8ac8206` headline `resolves #N` form auto-closed `#70` (2026-05-11); commit `a82867d` headline `fix #N` form auto-closed `#26` (2026-05-07). R1's body-only filter would have missed both. PR #98 6-AI verified in 2 rounds: R1 CONDITIONAL PASS (5/6, 1 HIGH blocking DA-H1 + Codex Finding 2); R2 6/6 PASS with Devil's Advocate explicitly recommending MERGE. Also adds `### 引用 trap pattern 作反例的寫作紀律` subsection under `## Commit Conventions` in `plugins/issue-driven-dev/CLAUDE.md` codifying the writing discipline: code fence is **visual only** (parser is context-blind), literal letter N (capital, no digit) is the **actual suppression** mechanism mirroring the safe pattern in `references/pr-flow.md:127`, cite-via-link is strongest. Single/double quotes do NOT suppress the parser. This is the write-time root fix; Source 2 is defence-in-depth at verify time. Step 0 bootstrap TaskCreate entry renamed `scan_pr_body_trailers` → `scan_pr_body_and_commits_trailers` reflecting Source 2. Backward compat: Source 2 is additive — clean PRs see no change in output; PRs with trap pattern in commits (subject or body) now get a warn block with fix-options (rebase + amend with letter N, override squash message via `gh pr merge --body`, or post-hoc `/idd-close`). Dogfood: PR #98's own squash commit (`e0d61e7`) is clean under the new 2-source Step 0.8 — the discipline added to CLAUDE.md held for both commit message body AND headline. Master verify reports at PR #98 #issuecomment-4483847549 (R2) and #issuecomment-4483788120 (R1). v2.60.1: cluster fix — PR-body auto-close trap (PsychQuant/issue-driven-development#87 + #74). All 4 IDD skill PR-body templates (idd-implement Step 5.5, idd-all Phase 5, idd-all-chain Step 5.5, pr-flow.md canonical) reworded to drop literal `Closes #${N}` from anti-trailer warnings — heredoc `${N}` substitution previously turned the cautionary warning string into a real `Closes #` that GitHub auto-close parser matched context-blind (ignoring negation, markdown, quotes), bypassing /idd-close's checklist gate + closing summary. New unified wording: `**Do NOT add a GitHub close trailer** (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.` Keywords named but never followed by `#` so GitHub's regex cannot match. NEW idd-verify Step 0.8 preventive gate (PR mode, warn-only): queries `gh pr view --json closingIssuesReferences` — GitHub's authoritative parse of which issues the PR auto-closes on merge — covering all trailer forms (Closes #N / Closes: #N colon form / cross-repo / issue-URL) without self-written regex. Warn-only since a PR body may legitimately quote the keywords in prose; gate value is making the risk visible at verify time before merge. Eventual-consistency caveat disclosed (closingIssuesReferences is settled state, settles well within typical verify-after-implement gap). PR #94 6-AI verified in 3 rounds: R1 (initial regex form) caught colon-form gap → R2 redesigned to closingIssuesReferences eliminating self-parser fragility → R3 cleanup (deleted orphan regex doc, surfaced gh failures with explicit skip note replacing silent fail-open, scoped overclaim, query `.url` not `.number`). Confirmed prior incidents resolved: #559, che-apple-mail-mcp#99, #73, #56. Two follow-up issues filed: #96 (cluster-PR mode silently forces PR path while direct-commit honours `--no-pr` — doc contradiction in pr-flow.md canonical algorithm table, design decision pending), #97 (new failure mode discovered at squash-merge of PR #94 itself — squash commit body inherited commit-message body quoting `Closes: #87` as a verify-finding reference, triggered auto-close of #87 2s after merge; Step 0.8 only scans PR body and doesn't predict squash commit message). Master verify report at PR #94 #issuecomment-4482808720. v2.60.0: idd-all-chain multi-root + DFS/BFS traversal + per-root halt + spawn-manifest schema v2 hard-break (PsychQuant/issue-driven-development#46, multi-root-traversal-idd-all-chain Spectra change). NEW multi-root invocation `/idd-all-chain #A #B #C [--bfs] [--cwd ]` accepts ≥1 root issue (N=1 byte-equivalent backward compat with v2.55.0+). NEW `--bfs` flag selects BFS traversal mode (push-back queue semantics for fairness across roots); default DFS pushes spawns to queue front (rich subtree first per root). NEW spawn manifest schema v2 hard-break: top-level `root_issue: int` → `root_issues: [int]`, top-level adds `traversal: \"dfs\"|\"bfs\"`, every spawn entry adds `root_id: int` (must match one of root_issues elements). Helper `scripts/manifest-append.sh` bumps `EXPECTED_SCHEMA_VERSION` 1→2, accepts 9th positional arg `root_id`, validates root_id ∈ root_issues array, fail-fast on v1 manifest detection. Cap redesign for multi-root accommodation: per-root `chain_max_depth` 2→3 (each root subtree counts depth from 0 independently), global `chain_max_issues` 5→10 (union across all root subtrees, applies independently of depth cap). Verify FAIL = per-root halt (D4 Option C): failing issue's `root_id` added to FAIL_ROOTS, all same-root pending issues purged from QUEUE, other root subtrees continue processing, commits preserved; Phase 4 emits per-root PASS/FAIL/SKIPPED summary block. Branch naming dispatches on N: N=1 keeps backward-compat `idd/chain--`, N>1 uses `idd/chain-multi--` where hash8 is first 8 hex of sha256 over sorted-asc root numbers joined by `-` (deterministic per root set); hash8 collision fallback hash16, double collision aborts with manual cleanup hint. PR title dispatches: N=1 `chain: `, N>1 `chain (multi-root): N issues — `. PR body cluster overview table adds `root_id` column; Refs lists all roots first then chained spawns. NEW Phase 4 forest tree printout: per-root subtree with status icons (✓ PASS, ✗ FAIL, ⊘ filed-but-not-chained), depth labels, spawn-source attribution; per-root PASS/FAIL summary block; filed-only-not-chained list. 4 sub-skills (idd-implement Step 5.7 / idd-verify Phase 4 / idd-plan Step 2.5 / idd-diagnose Step 3.6) propagate root_id via `IDD_CHAIN_CURRENT_ROOT_ID` env var (exported by Phase 2 chain loop before each `/idd-all #M --in-chain` invocation), with defensive `[ -n \"$ROOT_ID_FOR_MANIFEST\" ]` guard preventing silent skip when both env and local fallback variables are unset. NEW `allowed-tools` frontmatter expanded with 11 additional Bash tools (shasum/sed/tr/cut/sort/seq/grep/awk/printf/date/head/tail/wc/basename/comm) for Phase 0.5 branch naming + Phase 4 forest tree rendering. Modified `idd-all-chain` + `idd-spawn-manifest` specs (3 MODIFIED + 1 ADDED requirement each); spec deltas in openspec/changes/multi-root-traversal-idd-all-chain/. Updated `references/spawn-manifest.md` v2 schema doc + `references/chain-flow.md` DFS/BFS algorithm + per-root halt + cap interaction + branch naming hash rule sections + PR title/body dispatch. Backward compat: single-root chain invocation byte-equivalent to v2.55.0 except for the schema bump (v1 manifests on disk become unreadable — per design, manifest is transient per-chain-session state, hard-break safe). Smoke tests 7.1+7.2 marked `[~]` first-real-use validation track per `## Checklist Conventions` IDD discipline (orchestration tests cannot mock GitHub API + git operations without significant fixture infrastructure, mirroring #52 idd-verify validation pattern). v2.59.0: idd-verify orchestration playbook — Step 2 spawn restructure + NEW Step 2.5 Recovery Protocol (PsychQuant/issue-driven-development#52, resolves #70 structurally). Step 2 switches from TeamCreate (5 teammates with Read/Grep/Glob/Bash tools, NO Write) to 5 parallel Agent(subagent_type=general-purpose) calls (含 Write tool) + 1 Bash codex background, single-message dispatch preserves parallelism. Each reviewer prompt mandatorily contains 3 elements: (1) explicit findings file output path `Write findings to /tmp/verify__findings_.md`, (2) explicit 'DO NOT idle without producing output' rule, (3) retry-context-re-paste hint ('treat later SendMessage with re-pasted prompt as retry signal'). Pre-spawn prompt persistence: coordinator MUST save each role's prompt to /tmp/verify__prompt_.md before invoking Agent — Step 2.5b retry reads this file for FULL context re-paste (never assumes context survived idle/wake cycle, per #47 incident root cause). Devil's Advocate sequencing: bash polling loop on sibling findings files (max 30 iter × 5s = 2.5min timeout) replaces TeamCreate wait_for_idle primitive; timeout fallback writes SENTINEL marker `[STAGE 2.5 RECOVERY: DEVILS_ADVOCATE_TIMEOUT_/4]` on first line + body explanation. NEW Step 2.5 Recovery Protocol section between Step 2 spawn and Step 3 merge: (2.5a) file existence check scans 5 findings files; detects DA timeout sentinel via head -1 | grep then rm -f the file + add to MISSING_ROLES so downstream -s checks see role as missing; (2.5b) retry with FULL context re-paste using saved prompt file + 90s polling; (2.5c) second-idle coordinator self-review fallback; (2.5d) explicit 'Process Gaps' section in master report — no silent engine degradation. Step 3 merge prose source tag swept `[team:...]` → `[agents:...]`; ASCII architecture tree + 鐵律 rule updated; CLI alias `team` preserved backward-compat with documented backend as 5 standalone Agent calls. Frontmatter: TeamCreate removed from allowed-tools (no longer used). Side effect: #70 (TeamDelete cleanup gap on idle teammates from #47 verify-pr58 cycle) structurally dissolved — no team to delete = no cleanup gap. Plan tier D1-D5 + D6 first-real-use validation track: 3 codex verify rounds (R1 3 P1 → R2 2 P1 → R3 PASS) under codex-only degraded mode (Anthropic API rate-limit blocked Claude reviewer ensemble throughout session — dogfooded as Process Gap on first-real-use). Empirical bash smoke validated DA sentinel writer + Step 2.5a head -1 | grep detection + rm -f sequence. v2.58.0: idd-issue Stage 4.5 — jsonl gitignore pre-flight gate (PsychQuant/issue-driven-development#55). NEW pre-flight gate at idd-issue/SKILL.md between Stage 4 Dispatch and JSONL write: detects `.gitignore` shadowing of `.claude/.idd/issue-runs/.jsonl` via `git check-ignore -v` (D2 spec contract preservation). Source-aware classification via `IS_NESTED_GITIGNORE` flag — case statement orders absolute path / `.git/info/exclude` / bare `.gitignore` BEFORE `*/.gitignore` so global `core.excludesfile` named `.gitignore` does NOT mis-classify as nested. AskUserQuestion branches: Case A (fixable: root `.gitignore` / `.git/info/exclude` / global) → 3-option Add carve-out / Skip / Abort; Case B (nested `.gitignore`) → 2-option Skip / Abort with complete manual-fix chain hint (root rewrite cannot override per-directory ignore; nested file requires its own 4-line chain with trailing slashes on dir patterns + explicit `!.idd/issue-runs/*` glob, empirically validated). Universal 5-line carve-out block with `!.claude` parent re-include leverages git's last-matching rule to neutralize ANY outer ignore source — survives multi-source stacked ignores (root + `.git/info/exclude` / root + global / `.git/info/exclude` + global). Idempotent + upgrade-safe via two-part check (marker AND `!.claude` content presence): stale 4-line block (same marker, missing `!.claude`) triggers awk two-state-machine upgrade — STATE 1 consumes # rationale comments adjacent to marker; STATE 2 consumes only known carve-out literal lines, ENDS skip immediately after final pattern `!.claude/.idd/issue-runs` — adjacent user content (blank lines, user `# Section` comments, sibling patterns) preserved across all variants. Empty body's grep idiom uses `grep | wc -l | tr -d ' '` (clean integer) instead of `grep -c || echo 0` (which doubled output to `0\\n0` on no-match). Dispatch summary surfaces ignore source + user choice + continuity status (committed / pending exception / ⚠ local-only with manual export hint / aborted). Ordering invariant: dispatch → gate → materialize — Stage 4 loop accumulates in-memory RUN_LOG_ENTRIES, Stage 4.5 gate fires after loop completes, materialize phase decides jsonl write fate per `JSONL_GITIGNORE_DECISION`. Abort discards in-memory entries BEFORE materialization; already-dispatched GitHub actions NOT rolled back (user-confirmed intent per Stage 3). Env var bypass `IDD_JSONL_GITIGNORE_GATE=false` for CI/unattended with 1-line audit cite. Plan tier D3 evolved through 3 revisions (single-line → 4-line → universal 5-line) across 7 codex verify rounds (R1: 4 P1 → R2: 3 new P1 → R3: 2 new P1 → R4: 2 new P1 + 1 residual → R5: 3 new P1 → R6: 2 new P1 → R7: PASS), cumulative 16 P1 caught + fixed under codex-only degraded mode (Anthropic API limit blocked Claude reviewer team). Empirical 15/15 smoke validation across all source-classification scenarios (single source / stacked sources / nested / stale upgrade / idempotency / fresh / env bypass / skip-commit / abort). PR #71 squashed as `c342aa2`. Master verify report at PR #71 #issuecomment-4421108494. v2.57.0: idd-close Step 6.5 — Distribution Sync chain (PsychQuant/issue-driven-development#45). NEW Step 6.5 inserted between Step 6 (auto-update phase=closed) and Step 7 (batch close special rules), surfacing user-facing distribution channel sync (plugin marketplace / MCP binary / CLI binary) at issue close moment. Detection-driven AskUserQuestion 3-option pattern (per IC_R011 canonical): (a) `chain to now` invokes `/plugin-tools:plugin-update ` / `/mcp-tools:mcp-deploy` / `/cli-tools:cli-deploy`; (b) `skip — manual later` records `### Distribution Sync Pending` audit + manual command; (c) `not applicable` records reason. Detection helpers (inlined in Step 6.5 + canonical contract in references/distribution-detection.md): `is_plugin_marketplace_member` walk-up scan ancestor `.claude-plugin/marketplace.json` parse `plugins[].source` (string `\"./plugins/\"` form) + `has_binary_wrapper` line-agnostic scan `bin/*.sh` for GitHub release URL patterns + `resolve_plugin_name` extract matched plugin name for chain command composition + `infer_distribution_type` orchestrator returning plugin/mcp/cli/plugin+mcp/plugin+cli/n/a. Detection-based silent skip for non-distribution repos (always-on). `IDD_DISTRIBUTION_SYNC_PROMPT=false` env var bypasses prompt for distribution-detected repos (1-line audit). D3 mixed-type v1: explicit ordering binary-deploy first → plugin-update second (idempotent regardless of plugin-update Phase 1.5 cascade availability per #66 audit). Step 0.5 Bootstrap Task List adds `distribution_sync_chain_detection` entry. Step 4 closing comment ID capture hardened (stdout-only + sed -n + explicit empty-check). NEW reference doc references/distribution-detection.md. Complements `common-release-flow.md` (release-tier trigger) at close-tier window. Verify discipline survived 3 rounds + degraded engine (Anthropic API limit) — Codex CLI carried + Round 1 had regression + devil's advocate (3 sources convergent), 5 P1 → 0 P1 at Round 3. 2 follow-ups filed: #66 D3 audit (mid-plan tangential), #68 monorepo host disambiguation (round-3 advisory). Backward compat: non-distribution repos see zero behavior change. v2.56.0: idd-issue multi-finding source mode (PsychQuant/issue-driven-development#48, add-multi-finding-source-mode-to-idd-issue Spectra change). Auto-trigger on Step 1 source extracting ≥2 paragraph-level findings from docx/pdf/Telegram/Apple Mail/Apple Notes/pasted-text/md adapters. 4-stage pipeline: Stage 1 Extract verbatim quotes + AI summary; Stage 2 Per-finding picker with AI surface top-3 candidates via gh issue list --search keyword overlap (title×2 + body[:300]×1) + 4-option AskUserQuestion + intent disambiguation [comment/edit body/update status/skip] for picked existing #N + [Other] expands to [New issue/Skip/Merge/Pick free-text]; Stage 3 Batch preview single AskUserQuestion [Execute all/Edit row N/Cancel]; Stage 4 Dispatch with warn-continue (failures log to jsonl actions[i].error + retry_hint, no abort, no rollback). Audit trail dual-track: per-action body footer `> Surfaced via /idd-issue multi-finding mode from ` + structured JSONL at `.claude/.idd/issue-runs/.jsonl` committed to git for cross-machine continuity. Two-way merge via inline sub-prompt (partner picker + combined target picker), JSONL records merged_from / merged_into bidirectionally; three-way+ refused. NEW override flags `--multi-finding` (force mode) / `--no-multi-finding` (force fall-through); mutually exclusive with each other and with `--bundle-mode` (different mental models: bundle = explicit ordered/unordered creation; multi-finding = source-driven mixed routing). NEW capability `idd-issue-multi-finding-source` parallel to existing `idd-issue-bundle` (both extend idd-issue with non-overlapping modes). Cross-reference updates to idd-comment/idd-edit/idd-update SKILL.md adding \"When to use idd-issue multi-finding mode instead\" sections redirecting batch source workflows. Backward compat: single-issue invocations unchanged byte-equivalent; --bundle-mode invocations unchanged; auto-trigger threshold is ≥2 detected findings else fall through. 5 architectural decisions D1-D5 from spectra-discuss session 2026-05-10 + 2 derived D6 trigger detection / D7 mutual exclusion in design.md. v2.55.0: NEW /idd-all-chain skill — chain-solve mode (PsychQuant/issue-driven-development#44, add-idd-all-chain-skill Spectra change). Drives root issue + auto-emergent spawned issues (sub-skill sister sweeps / verify follow-ups / mid-plan tangentials / sister concerns) through ONE cluster branch + ONE review PR. NEW skill /idd-all-chain #N: thin shell over /idd-all, internally recursive-invokes /idd-all #M --in-chain. Phase 0 creates cluster branch idd/chain-- from default branch + initializes spawn manifest at .claude/.idd/state/chain-spawned-issues.json (schema_version=1, atomic temp-file rename writes). Phase 2 main loop pops queue, invokes sub-/idd-all, reads manifest delta, enqueues chain-eligible spawns (rule: same_file_as_root OR same_skill_as_root OR spawn_kind='sister-bug'). Phase 3 opens cluster PR (title prefix 'chain:', collapsed
per issue, Refs all chained, Pending review checklist forbidding Closes/Fixes/Resolves trailers per IDD discipline). Phase 4 STOPs at verified — no auto-close, no auto-merge (per-issue /idd-close required). NEW --in-chain flag on /idd-all: single source for chain context, derives 4th mode tuple (direct-commit, unattended). Sub-/idd-all skips Phase 0.5 PR-mode branch creation + skips Phase 5.5 PR open + sub-skills receive UNATTENDED MODE directive. --in-chain mutex with --pr/--no-pr. NEW spawn manifest cross-skill contract: 4 sub-skills (idd-implement Step 5.7 sister bug sweep / idd-verify Phase 4 follow-up findings / idd-plan Step 2.5 tangentials / idd-diagnose Step 3.6 sister concerns) all conformantly write entries with classify spawn_kind + same_file_as_root + same_skill_as_root flags. Helper script scripts/manifest-append.sh implements atomic write + schema_version mismatch abort. Hard caps: chain_max_depth=2, chain_max_issues=5 (incl. root) — over-cap spawns still file as follow-up issues but not enqueued. Failure mode: any chained verify FAIL halts queue + preserves partial commits on cluster branch (no rebase/revert) + abort report cites 4 recovery paths. MODIFIED capability idd-orchestrator-modes: 4th mode tuple (direct-commit, unattended) added for chain context; existing 3 tuples behavior unchanged. NEW reference docs: references/spawn-manifest.md (schema canonical contract) + references/chain-flow.md (chain shell algorithm canonical contract incl. eligibility rule + caps + failure mode + PR body schema). Backward compat: /idd-all #N without --in-chain flag is byte-equivalent to v2.53.0 baseline. v2.52.0: idd-issue ordered/unordered bundle flags (PsychQuant/issue-driven-development#21). NEW `--parent ` flag PATCHes parent issue's body task list with new child entry, idempotent via `#N` reference scan + fallback `## Children` anchor when no list exists. NEW `--blocked-by [,...]` flag applies three-layer fallback chain: Layer 1 GraphQL `addBlockedByDependency` mutation attempt (graceful failure → warning + continue, no abort) + Layer 2 unconditional body blockquote `> Blocked by #M` (always readable in any markdown viewer) + Layer 3 parent task list annotation `(blocked by #M)` when `--parent` co-used. NEW `--bundle-mode ` flag orchestrates bundle creation in single invocation: builds 1 epic parent + N children with auto-applied `--parent `, ordered mode adds strict `child[i] blocked by child[i-1]` chain, unordered keeps task list only. Pre-flight gates: cross-repo refuse (parent in different repo than resolved target → abort + redirect to `groups` mechanism), bundle-mode and group-mode mutual exclusion (different mental models, refuse if both set). Step 3.B inserted between 3.A (single repo) and 3.G (group cross-link), reusing 3.A flow as primitive. Orthogonal with Step 4.5 milestone (bundle children get milestone assignment), Step 4.7 sister sweep (parent epic still subject to sweep, sibling issues NOT added to bundle task list). NEW canonical reference doc references/bundle-flags.md (flag spec + edit algorithm + fallback chain + partial failure + idempotency contract). NEW `## Ordered Bundle Pattern` section after Step 5 in idd-issue SKILL.md (3-mode comparison table + 3 usage scenarios + design rationale for not creating separate /idd-bundle skill). Step 0 Bootstrap Task List adds `resolve_parent_link`, `apply_blocked_by`, `orchestrate_bundle_mode` TaskCreate entries. NEW capability `idd-issue-bundle` in openspec/specs/. No breaking changes — all flags additive, omitted invocation behavior unchanged. Spectra change `add-bundle-flags-to-idd-issue` in this repo's openspec/changes/. v2.51.0: idd-list shows open PR info per issue + cluster detection (PsychQuant/issue-driven-development#13). NEW Step 2.5 batch fetches all open PRs once via 'gh pr list --state open --limit 100'; NEW Step 3.5 client-side regex-scans PR bodies for '#N' refs and builds reverse issue→PR index plus cluster map (PRs ref'ing 2+ issues). Step 4 Format Output extended: each issue with a PR ref gets a sub-line '└─ PR #N (status, mergeable)'; cluster leaders (lowest issue number in refs) show 'cluster: #X #Y #Z' listing all members; cluster members show '→ see PR #N (cluster member)' redirect. Direct-commit issues (no PR refs) display unchanged from v2.50 — fully backward compatible. Footer adds second line summarizing 'N issues bundled in M cluster(s); P solo PR(s); Q direct-commit'. Step 5 Suggest Next extended to phase × PR state matrix (10+ rows): implemented + draft → 'gh pr ready N → /idd-verify --pr N'; implemented + ready MERGEABLE → '/idd-verify --pr N'; verified + ready MERGEABLE → 'gh pr review N → gh pr merge N → /idd-close #N'; verified + merged catch-up → '/idd-close #N'; CONFLICTING → 'gh pr checkout N → resolve'; cluster member → 'see leader's next action'. Sister concerns filed as future P3 follow-ups: #14 (markdown-aware PR body parser to ignore '#N' inside fenced code blocks; v1 accepts false positive) + #15 (cluster_leader config 'lowest|primary' instead of hardcoded lowest). v2.50.0: Layer V Vagueness Pre-check (PsychQuant/issue-driven-development#12). NEW Step 3.4 in idd-diagnose between Layer 1 disqualifier and Layer 2 Spectra evaluation: AI scores V1 (vague WHAT) + V4 (vague ACCEPTANCE) on Likert 6-point scale (no neutral midpoint), trigger threshold per-axis ≥ 4. Triggered cases fire Hybrid 3-option AskUserQuestion (clarify now / proceed anyway / escalate to Plan) with default option score-driven (V=4 → proceed, V=5 → clarify, V=6 → escalate). 'clarify now' appends Q/A pairs to issue body via gh issue edit then re-runs Layer V; 'proceed anyway' continues to Layer 2/3/P with audit trail recording trigger fact; 'escalate to Plan' force-sets verdict = 'Plan via Layer V' and skips Layer 2/3/P. Layer evaluation order: Layer 1 → V → 2+3 → P → Simple. Routing parsers in idd-implement Step 2.5 + idd-all Phase 3 strip ' via X' suffix to extract canonical tier — bare 'Plan' / 'Simple' / 'Spectra' verdicts unchanged (backward compat). NEW project rule .claude/rules/attribute-assessment.md codifies meta-principle 'attribute scoring SHALL use Likert scale, not keyword matching' — applies session-wide via root CLAUDE.md @import, scope beyond Layer V (any future attribute scoring need). MANIFESTO 5-axis bug-fix model expanded to 6-axis adding 'Alignment quality' (TDD ❌ / SDD ❌ / IDD ✅), evidence = Layer V. idd-all unattended mode auto-applies 'proceed anyway' + audit trail '[Layer V: V1=N V4=M, clarify-default skipped under unattended mode, defaulting to proceed]' (same pattern as Plan tier under unattended). Backward compat: pre-v2.50 diagnoses NOT retroactively re-evaluated; existing Simple / Plan / Spectra / SDD-warranted verdicts remain valid. No --ignore-vagueness flag (option B 'proceed anyway' covers that need). Spectra change add-vagueness-layer-routing in this repo's openspec/changes/. Step 0 Bootstrap Task List adds 'vagueness_precheck' TaskCreate. v2.49.0: references/ic-r011-checkpoint.md v1.1.0 — Third-Party Skill Alignment section for /spectra-discuss + /spectra-propose (kiki830621/ai_martech_global_scripts#530, sub-issue E of #523 systematic plugin alignment, last sub-issue closing the parent epic). spectra-* skills are published by third-party kaochenlong/spectra-app — direct SKILL.md modification not in this plugin's commit cycle. Documentation-side alignment: agents/users invoking /spectra-discuss + /spectra-propose with IC_R011 in mind apply the canonical 3-option AskUserQuestion + audit trail manually at deliberation-moment equivalents (discussion convergence / proposal drafting). Per canonical eligibility criteria §6, only the 2 deliberation-moment spectra-* skills (discuss / propose) need alignment; the other 6 (apply / archive / ask / ingest / commit / debug) are mechanical execution and N/A. If spectra-app upstream adopts native IC_R011 checkpoint, this section becomes redundant + can be removed. Strength: SHALL — discussion / proposal drafting are deliberation moments per canonical eligibility criteria. v2.48.0: idd-issue Step 4.7 — Linked-Context Sister Sweep (kiki830621/ai_martech_global_scripts#529, sub-issue D of #523 systematic plugin alignment). NEW advisory step between Step 4.5 (auto-milestone) and Step 5 (報告), scanning issue body draft + linked attachments + recent session conversation for sibling-concern markers (also / additionally / related / 另外 / 順便 / BTW). If hits, AskUserQuestion 3-option per canonical references/ic-r011-checkpoint.md (#525). 'file as sibling issues now' / 'file selected' files via 'gh issue create' as parallel issues (NOT cross-linked into the just-created issue body, since user's primary concern stays focused), each with confidence:confirmed + priority:P3 + source link 'surfaced during /idd-issue #NEW linked-context sister sweep (Step 4.7)'. PATCHes the just-created issue body to add '### Linked-Context Siblings Filed (v2.48.0+ #529)' audit trail per canonical heading conventions. Strength: SHOULD (advisory, non-blocking) per canonical eligibility criteria §6 — issue creation is light-touch (user is already in filing-active mode, double-prompt risks friction). Empty list = silent no-op default. AI_LOW_BAR_ISSUE_FILING=false env var skips silently per IC_R011 rollback hatch. Step 0 Bootstrap Task List adds 'linked_context_sister_sweep' TaskCreate. v2.47.0: idd-diagnose Step 3.6 — Sister Concern Surfacing (kiki830621/ai_martech_global_scripts#528, sub-issue C of #523 systematic plugin alignment). NEW mandatory step between Step 3.5 (Complexity Assessment) and Step 3.7 (Agent Routing), surfacing sister-concern markers in just-posted Diagnosis content (也有 / sister / 同樣的 / 另外 / likewise affects) + scout session log. AskUserQuestion 3-option per canonical references/ic-r011-checkpoint.md (#525). 'file all/selected' files via 'gh issue create' with confidence:confirmed + priority:P3 + source link, then PATCHes Diagnosis comment with '### Sister Concerns Filed (mid-diagnose, v2.47.0+ #528)' audit trail per canonical heading conventions. Strength: SHALL (mandatory step) per canonical eligibility criteria — diagnosis is a deliberation moment where sister concerns naturally surface during Strategy authoring. Empty list legitimate. AI_LOW_BAR_ISSUE_FILING=false env var skips silently per IC_R011 rollback hatch. Step 0 Bootstrap Task List adds 'sister_concern_surfacing' TaskCreate. v2.46.0: idd-all HITL mode (PsychQuant/issue-driven-development#1). Phase 0.5 mode resolution from existing pr_policy + new --pr/--no-pr flags into (path, interaction) tuple — PR + unattended (v2.40.0 regression — /loop friendly) or direct-commit + attended (HITL — solo/personal repos where PR is ceremony, user is in keyboard, sub-skill AskUserQuestion / EnterPlanMode / Park-Apply prompts fire natively). Two axes from one source (no duplicate config surface). v2.45.0: idd-close Step 3.5 — Closing Summary Follow-up Keyword Scan (kiki830621/ai_martech_global_scripts#527, sub-issue B of #523 systematic plugin alignment). NEW step between Step 3 (review with user) and Step 4 (gh issue close), scanning drafted closing summary for trigger phrases (follow-up / deferred / future / TODO / later / 之後 / 未來 / 順便 / 我之前觀察到 / 之後再 / 改天). Each match is checked against existing #NNN cross-links via 'gh issue view' — orphan mentions (no link or stale link) trigger AskUserQuestion 3-option per canonical references/ic-r011-checkpoint.md (#525). 'file all/selected' files via 'gh issue create' with confidence:confirmed + priority:P3, then PATCHes closing summary inline (mention → '...(see #NEW)') and adds '### Closing Follow-ups Filed (v2.45.0+ #527)' audit trail. Strength: SHOULD (advisory, non-blocking) per canonical eligibility criteria — closure is mostly mechanical action; surfacing orphan-mention pattern at decision moment without forcing filing. AI_LOW_BAR_ISSUE_FILING=false env var skips silently per IC_R011 rollback hatch. Disambiguation note added: this Step 3.5 is the IC_R011 checkpoint; Step 0 supersession (#515 v2.41.0) is gate logic — orthogonal concerns. Step 0.5 Bootstrap Task List adds 'closing_followup_keyword_scan' TaskCreate. v2.44.0: idd-implement Step 5.7 — Sister Bug Sweep (kiki830621/ai_martech_global_scripts#526, sub-issue A of #523 systematic plugin alignment). New mandatory step between Step 5.5 (Open PR if PR path) and chain to /idd-verify, surfacing sister bugs discovered during TDD reproduction (Step 3) — adjacent same-root-cause sibling files like the proven 2026-05-03 #510 → #518 → #520 cluster (gen_*.R / fix_wiser_poisson_tables.R / _build.R) where each manual reminder was needed despite same pattern. Cites canonical references/ic-r011-checkpoint.md (#525) for 3-option AskUserQuestion (file all / file selected / skip) + heuristic triggers + audit trail format + AI_LOW_BAR_ISSUE_FILING=false rollback hatch. PATCHes Implementation Complete comment to add `### Sister Bugs Filed (mid-impl, v2.44.0+ #526)` audit trail per canonical heading conventions table. Strength: SHALL (mandatory step) but empty list legitimate. Step 0 Bootstrap Task List adds `sister_bug_sweep` TaskCreate entry. v2.43.0: NEW canonical reference doc references/ic-r011-checkpoint.md (kiki830621/ai_martech_global_scripts#525, sub-issue F of #523 systematic plugin alignment). Standardizes the 3-option AskUserQuestion pattern (file all / file selected / skip), heuristic triggers (verifiable behavior gap / sister bug from reproduction / observed friction / deferred work / out-of-scope user mentions / drift / TODO encounters), default-off exemptions (pure exploration / existing issue / hallucinated / CONSTRAINT / mechanical execution stages), audit trail per-skill heading conventions, rollback escape hatch (env var + repo CLAUDE.md flag), and eligibility criteria (which skills SHALL vs SHOULD vs N/A). Cited from idd-plan Step 2.5 (#524) + idd-close Step 0 supersession (#515) — both back-link the canonical doc. Sister sub-issues #526-#530 (idd-implement / idd-close closing summary scan / idd-diagnose / idd-issue / spectra-discuss + spectra-propose) will all cite this canonical doc when their Plan tier ships;cross-skill consistency mechanically anchored. v2.42.0: idd-plan Step 2.5 — mid-plan tangential observations sweep (kiki830621/ai_martech_global_scripts#524 fix). Plan-tier deliberation surfaces tangential discoveries (Phase 1 Explore agents pass-by sister bugs, Phase 2 grep-discovered drift, Phase 3 user-mentioned sub-concerns) that previously fell into the gap between In-scope and Out-of-scope categorization, vanishing into conversation. New mandatory step between Step 2 (Draft Plan) and Step 3 (Confirm post): agent reviews session log, surfaces candidates with IC_R011 default-on heuristic (verifiable behavior gap / sister bug / out-of-scope user-mentioned), AskUserQuestion three-option (file all / file selected / skip), files via 'gh issue create' with confidence:confirmed + priority:P3 + source link to plan issue, then PATCHes plan body to add '### Tangential Observations' audit trail. Empty list = no-op (legitimate). AI_LOW_BAR_ISSUE_FILING=false env var skips per IC_R011 rollback hatch. Codifies IC_R011 spirit at the mid-plan window — finer gap than #523 broader systematic alignment. v2.41.0: idd-close Step 0 supersession of pre-implementation Strategy / Implementation Plan checkboxes (kiki830621/ai_martech_global_scripts#515 fix). When `## Implementation Complete > ### Checklist` exists and all its items are `- [x]`, that subsection is treated as the canonical state of truth — `Strategy` / `Implementation Plan` `- [ ]` items are auto-superseded (skipped from gate). Resolves the recurring friction where work was complete but `idd-implement` Step 5 only synced its own Implementation Complete comment (never PATCHed Strategy / Plan comments), leaving 8+ pre-impl `- [ ]` items blocking gate and forcing manual `gh api PATCH` workaround on every full-lifecycle close (#455 / #510 close, 2026-05-03). Defensive properties preserved: incomplete Implementation Complete (any `- [ ]` remaining) falls back to legacy full spec scan; legacy issues without Implementation Complete unchanged. Strategy A from #515 diagnosis (chosen over B sync-at-write and C narrow-gate). v2.40.0: --cwd flag propagated to all sub-skills (idd-diagnose / idd-implement / idd-verify) so cross-repo orchestration via idd-all actually works end-to-end. v2.39.0 added --cwd to idd-all only — but sub-skills still inherited Claude Code session cwd, so idd-implement would commit to the wrong repo. NEW: shared `references/cross-repo-cwd.md` documents the substitution rule (`git X` → `git -C $CWD X`, `gh issue/pr/repo X` → `gh ... X -R $GITHUB_REPO`) once; each sub-skill cites it at the top of Execution. NEW: idd-all Phase 1/2/3a/4 forward `--cwd $CWD` to sub-skill args; Phase 1 (idd-issue) and follow-up-issue creation use `--target $GITHUB_REPO` instead (read-only, no local git needed). Backward compat: omitting --cwd reads session pwd (existing behavior). v2.39.0: idd-all --cwd flag + cross-repo invocation. NEW Step 0.2 Resolve Working Tree resolves target repo from --cwd /path/to/clone (per-invocation override) instead of hardcoded session cwd. All git ops use 'git -C $CWD'; all gh ops use 'gh -R $GITHUB_REPO' (repo derived from origin remote). Solves 'Skill tool inherits Claude Code session-level cwd, can't follow mid-session cd' friction when running idd-all on a repo other than the one your session started in (e.g. thesis work in repo A, want pipeline on dependency repo B). Phase 0.2/0.3 abort messages now include explicit 'pass --cwd /path/to/clone' alternative. Backward compat: omitting --cwd uses session cwd (existing behavior). v2.38.0: idd-route integration — data-driven agent routing recommendation from observed track record. NEW idd-diagnose Step 3.7: if ~/bin/idd-route is available, calls it with current issue's complexity + estimated scope LOC + extracted signals to get an agent recommendation (codex-gpt-5.5-xhigh / claude-opus-4.7 / sonnet-4.6 / haiku-4.5), injects 'Recommended Agent' section into diagnosis comment. NEW idd-verify Step 5d: records each verify outcome (issue, agent, complexity, scope, round trips, blocking findings, follow-ups) to /.claude/.idd/routing-stats.jsonl + global mirror. NEW idd-close Step 4.5: appends final outcome (merged/abandoned) — append-only so original in_review record stays for audit. All three are gracefully no-op when idd-route binary missing (command -v check). Companion plugin idd-route ships in same marketplace; binary source at PsychQuant/idd-route-swift. NEW references/agent-routing.md is canonical contract for the IDD ⇄ idd-route boundary. Built on top of v2.37.0's external-agent / PR mode foundation — the routing recommendation closes the loop: idd-diagnose suggests agent → user delegates → idd-verify records outcome → next idd-diagnose recommendation gets smarter. Plus marketplace migration: this is the first issue-driven-dev release in the new PsychQuant/issue-driven-development marketplace (formerly lived in psychquant-claude-plugins; full git history preserved via filter-repo). v2.37.0: External-agent / PR mode for idd-verify + use-case routing reference. NEW idd-verify --pr input mode for verifying PRs opened by external agents (Codex via codex exec, Copilot Workspace, remote claw on PsychQuantClaw) — gh pr diff + gh pr checkout so reviewer agents see file context, auto-restore original branch after verify. Plus --commits N / --since / --branch flags for other input sources. Auto-detect mode (no flag): counts unpushed Refs #N commits since origin/ first; if 0, queries open PRs ref'ing #N and AskUserQuestion to pick between local diff vs PR — catches the common 'forgot --commits N' case without silently switching modes. Issue↔PR correspondence is a hard iron rule: PR mode aborts before invoking 6-AI ensemble if PR body has zero Refs #N (untrackable change violates IDD discipline) or if user-passed issue isn't in PR's Refs set (correspondence broken); discovers superset triggers AskUserQuestion to confirm scope. PR mode flips master comment location: full verify report posts to PR (external agent owners work in PR view, never see issue comments), each ref'd issue gets a 1-line pointer comment back with PASS/FAIL + master comment URL. Capture-master-URL-then-write-pointer SOP enforced (prevents the recurring bug class where pointer URLs accidentally reference earlier diagnosis/implementation comments). NEW references/external-agent-delegation.md is canonical contract: 4-phase delegation impact matrix (diagnose stays, implement may delegate, verify+close return to IDD), hands-off principle (no babysitting external agents; strict verify, opt-in fix takeover), 3 input modes + auto-detect algorithm, issue↔PR gate, PR-as-master cross-post, working tree handling, deferred items (--takeover, idd-handoff, force-push detection). NEW references/usecase-routing.md closes discoverability gap: 24-row table mapping common scenarios → exact skill chain + flags + contract doc (single / batch / cluster-PR / external-agent PR/commits/branch/auto / Plan tier / Spectra-warranted / bundle close / Spectra-bridge / multi-repo monorepo) plus top-of-doc decision tree for users unsure which entry point to start from. Linked from CLAUDE.md (Claude-facing) and README.md (human-facing). Backward compat: single-issue invocation idd-verify #42 without flags still works as v2.36 in common case (no Refs commits, no open PRs → falls back HEAD~1); cluster-PR mode #34 #36 #38 unchanged; no flag deprecations. v2.36.0: 3-tier Complexity routing (Simple / Plan / Spectra) + new idd-plan skill. SDD-warranted renamed to Spectra (backward-compat alias preserved). Plan tier inserts EnterPlanMode approval gate between diagnosis and TDD execution — covers 'think before leap, no spec contract needed' (most common case where Simple was too thin and Spectra was overkill). Issue-driven development methodology: issue → diagnose → (idd-plan if Plan tier) → implement → verify → close.", - "version": "2.69.0", + "version": "2.70.0", "author": { "name": "Che Cheng" }, diff --git a/plugins/issue-driven-dev/CHANGELOG.md b/plugins/issue-driven-dev/CHANGELOG.md index 80dbfde..bf7e7a8 100644 --- a/plugins/issue-driven-dev/CHANGELOG.md +++ b/plugins/issue-driven-dev/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.70.0] - 2026-05-20 + +### Fixed + +- **`idd-issue` Step 1 pasted-image immediate-persistence** ([#112](https://github.com/PsychQuant/issue-driven-development/issues/112)): Claude Code's `~/.claude/image-cache//` is per-session + cleared by context compaction / session lifecycle / session-id rollover. Step 1 → Step 4 separation (read annotation in Step 1, upload in Step 4) spans `AskUserQuestion` + Step 2.5/2.6 + Step 3 `gh issue create` + Step 4 upload — easily long enough for cache eviction. 2026-05-20 downstream incident (`kiki830621/ai_martech_global_scripts#788`) hit exactly this failure mode. NEW immediate-persistence rule: when Step 1 encounters `[Image: source: ]` annotation, `cp` to `/tmp/idd-issue-attachments/issue_pending__.png` in the **same tool turn** that reads the annotation; Step 4 references the staged path, not the original cache path. Anonymous `/tmp` staging (POSIX-safe, system-cleanup-friendly, no repo pollution) per `feedback_lead_minimal`. Fallback for already-evicted source: warn + continue without that attachment. + +### Refactored + +- **`spectra-archive` skill `.agents/` ↔ `.claude/` sync** ([#93](https://github.com/PsychQuant/issue-driven-development/issues/93)): #93 surfaced 3-copy divergence between `.claude/skills/`, `.agents/skills/`, and `plugins/.../references/spectra-skills/`. Investigation refuted the diagnose-time recommendation to delete `.agents/` — 4 openspec specs reference `.agents/skills/spectra-*/SKILL.md` as Spectra-tier dependencies (the path is LIVE, not legacy). Revised disposition: sync `.agents/skills/spectra-archive/SKILL.md` from `.claude/` so both LIVE load paths carry the v1.3+ Implementation Complete auto-post feature (#56). `plugins/.../references/spectra-skills/spectra-archive/` left as historical snapshot (no markdown cross-refs found; low cleanup ROI per `lead-minimal`). **Sister-skill divergences out of scope**: 7 other spectra-* skills (audit / discuss / propose / apply / ingest / debug / commit) also diverge between `.claude/` and `.agents/` — audit comment on #93 documents the drift matrix; each candidate filed for separate follow-up as needed. Drift-prevention CI hook deferred until drift recurs naturally. + +- **`idd-implement` cluster detection glob hardening + Option A-final doc** ([#100](https://github.com/PsychQuant/issue-driven-development/issues/100)): two non-blocking findings from PR #99 (#96) verify rounds. + - **Finding 1 (design)** — Option A (cluster mode unconditionally forces PR regardless of branch context) confirmed final. NEW `### Feature-branch + cluster + direct-commit — rejected case` subsection in `references/pr-flow.md` § Cluster mode override documenting the rejected Option B (branch-context-gated cluster direct-commit) with comparison table + rationale. Contract simplicity wins; feature-branch direct-commit workflow remains viable for single-issue `--no-pr` invocations. + - **Finding 2 (refactor)** — `idd-implement` Step 0.5 cluster detection bash hardened. Previous glob `\#[0-9]*` over-matched (`#42abc` counted, `#34 #34` over-counted as 2). Replaced with strict integer check (`[[ "$arg_num" =~ ^[0-9]+$ ]]`) + associative-array dedup matching the documented `^#\d+$` form in `batch-and-cluster.md`. 0 behavior change for well-formed distinct invocations. + +### Notes + +- Plugin v2.70.0 is a **minor** bump (over v2.69.0) covering 3 issues across `idd-issue` + `idd-implement` + `pr-flow.md` + `.agents/skills/spectra-archive/SKILL.md`. All changes additive (#112 immediate-persistence + #93 sync + #100 glob hardening + Option A-final documentation). Cluster PR for review surface — verify ensemble runs over the cumulative diff. +- Marketplace.json sync deferred to `/idd-close` Step 6.5 chain (per repo precedent). + ## [2.69.0] - 2026-05-20 ### Fixed From 8b157844533bed99f73f1238444729ef027eeb97 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 12:58:26 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20PR=20#115=20logic-reviewer=20finding?= =?UTF-8?q?s=20=E2=80=94=20mktemp=20+=20explicit=20PASTED=5FIMAGE=5FPATHS?= =?UTF-8?q?=20contract=20(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2 MEDIUM findings from logic reviewer dispatched in-PR per feedback_verify_fix_same_pr: #112-COLLISION (MEDIUM) — $(date +%s)_$RANDOM had ~0.4% collision rate at 1000 tight-loop trials. Replaced with mktemp which guarantees uniqueness atomically (POSIX create-or-fail). Both macOS BSD mktemp and GNU mktemp accept the 6-X template. mv after mktemp adds the .png extension while preserving the unique pathname. #112-CONTRACT (MEDIUM) — PASTED_IMAGE_PATHS array population responsibility was implicit. NEW prose paragraph above the staging bash block explicitly states: 'when the agent sees [Image: source: ] annotation(s), populate bash array PASTED_IMAGE_PATHS=(...) with one entry per annotation before invoking the staging loop. The annotation is the only authoritative source.' #112-STEP4 (LOW) — Step 4 hand-off contract made explicit with NEW prose paragraph + concrete bash snippet showing ATTACHMENT_PATHS+=("${PASTED_IMAGES_STAGED[@]}") concatenation. Eliminates ambiguity about how Step 1's staged paths flow to Step 4's upload loop. Also added `declare -a PASTED_IMAGES_STAGED=()` explicit declaration (prior version assumed bash default behavior; explicit is safer). Refs #112 --- .../skills/idd-issue/SKILL.md | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md index f977148..9ba470e 100644 --- a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md +++ b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md @@ -283,13 +283,17 @@ UPSTREAM=$(echo "$REPO_JSON" | jq -r '.parent.nameWithOwner // empty') **Rule (SHALL)**: when Step 1 encounters a `[Image: source: ]` annotation in the prompt, **`cp` the image to a stable staging path within the SAME tool turn** that first sees the annotation. Do NOT defer to Step 4. The staged path joins Step 4's upload list; the original `~/.claude/image-cache/` path is no longer referenced after Step 1. +**PASTED_IMAGE_PATHS source contract**: when the agent (Claude Code) sees `[Image: source: ]` annotation(s) in the user's prompt (one or more), populate a bash array `PASTED_IMAGE_PATHS=( "/Users/che/.claude/image-cache//1.png" ... )` with one entry per annotation before invoking the staging loop below. The annotation is the **only** authoritative source — there is no separate enumeration API; the agent reads the prompt text and extracts annotation `source:` values verbatim. + ```bash # Run in the SAME tool turn that first sees the [Image: source: ...] annotation. # Do NOT split into a separate turn — that's the bug class #112 surfaced. mkdir -p /tmp/idd-issue-attachments -# Track staged paths in an array for Step 4 to upload. -PASTED_IMAGES_STAGED=() +# Track staged paths for Step 4 upload. MUST be declared as array (not string) so +# Step 4's ATTACHMENT_PATHS+=("${PASTED_IMAGES_STAGED[@]}") concatenation works. +declare -a PASTED_IMAGES_STAGED=() + for src_path in "${PASTED_IMAGE_PATHS[@]}"; do if [ ! -f "$src_path" ]; then # Cache already evicted before Step 1 ran (rare — happens in compaction-resumed sessions @@ -299,15 +303,37 @@ for src_path in "${PASTED_IMAGE_PATHS[@]}"; do echo " Please re-paste the image OR provide a stable path; continuing without this attachment." >&2 continue fi - # Stage to /tmp with timestamp + random suffix (POSIX-safe, anonymous, no repo pollution). - # System /tmp housekeeping cleans these eventually; no manual cleanup required. - staged_path="/tmp/idd-issue-attachments/issue_pending_$(date +%s)_$RANDOM.png" + # Stage to /tmp via mktemp — collision-safe even under hostile concurrency. + # mktemp guarantees uniqueness (atomic create-or-fail per POSIX); pre-existing + # `$(date +%s)_$RANDOM` had ~0.4% collision rate at 1000 tight-loop trials + # (v2.70.0+ #112 logic-reviewer finding). mktemp closes that channel structurally. + # + # macOS BSD mktemp uses 6-X template by default; GNU mktemp accepts the same. Output + # is the actual unique pathname. The .png extension is appended after mktemp returns + # to preserve mime detection downstream. + staged_base=$(mktemp /tmp/idd-issue-attachments/issue_pending_XXXXXX) || { + echo "✗ mktemp failed for /tmp/idd-issue-attachments — skipping $src_path" >&2 + continue + } + staged_path="${staged_base}.png" + mv "$staged_base" "$staged_path" # rename so .png extension is present cp "$src_path" "$staged_path" PASTED_IMAGES_STAGED+=("$staged_path") echo "→ Staged $src_path → $staged_path" done ``` +**Step 4 hand-off contract**: Step 4's attachment-upload loop MUST concatenate `PASTED_IMAGES_STAGED[@]` into its `ATTACHMENT_PATHS[@]` upload list. Explicit example: + +```bash +# In Step 4, after gathering other attachment paths (from docx export_image / pdfimages / etc): +ATTACHMENT_PATHS+=("${PASTED_IMAGES_STAGED[@]}") + +# Then iterate ATTACHMENT_PATHS for gh release upload. +``` + +This makes the hand-off explicit — staged paths from Step 1's pasted-image handler flow through to Step 4 without a separate enumeration step. + **Step 4 reference contract**: when uploading attachments, iterate `PASTED_IMAGES_STAGED[@]` (NOT the original `[Image: source:...]` paths). The annotation in the prompt is only the *initial pointer*; the staged copy is the durable artifact. **Why `/tmp` not in-repo `.claude/.idd/issue-pending/`**: anonymous + system-cleanup-friendly + doesn't pollute version control. The in-repo alternative was considered but rejected per `feedback_lead_minimal` — system housekeeping handles cleanup without policy surface. From d63575a7d8eef66db51e92b973473c054383c367 Mon Sep 17 00:00:00 2001 From: che cheng Date: Wed, 20 May 2026 13:06:45 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20PR=20#115=20DA=20findings=20?= =?UTF-8?q?=E2=80=94=20CHANGELOG=20honesty=20+=20backward-compat=20note=20?= =?UTF-8?q?+=20qualifier=20(#93=20#100=20#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3 non-blocking findings from Devil's Advocate dispatched in-PR per feedback_verify_fix_same_pr: DA-1 (MEDIUM) — CHANGELOG #93 entry said "filed for separate follow-up as needed" but no sister-skill issues were actually filed. Misleading audit trail per IC_R011 canonical 'file / file selected / skip' semantics. Softened to "Sister issues NOT auto-filed in this PR per feedback_lead_minimal — drift documented as observation, separate issues will be filed if specific divergence causes user-visible friction". Cites DA finding DA-1 for traceability. DA-2 (LOW) — #100 cluster glob hardening had a quiet behavior change for malformed tokens (#42abc previously counted as cluster member; post-v2.70.0 silently skipped). CHANGELOG #100 entry extended with explicit backward-compat note: failure modes shifted from 'fail mid-loop on bad number' to 'treat as if token not present'. DA-4 (LOW) — softened the "ONLY authoritative source" claim in the PASTED_IMAGE_PATHS source contract from absolute to qualified-by-version: 'currently the only authoritative source per the v2.70.0+ Claude Code prompt format'. Adds explicit note that future Claude Code versions (drag-drop / other annotation forms) may require contract extension. Codex nit — table row + subsection wording updated from 'issue_pending__.png' to 'issue_pending_XXXXXX.png (via mktemp)' to match the post-fix mktemp-based impl (commit 8b15784). DA-3 (LOW) staged_base.png stale leftover — not fixed in this commit; mktemp guarantees fresh uniqueness so stale-overwrite is only possible under hostile pre-creation by attacker (out of scope for IDD threat model — no user-controlled write path to /tmp/idd-issue-attachments/). Refs #93 #100 #112 --- plugins/issue-driven-dev/CHANGELOG.md | 4 ++-- plugins/issue-driven-dev/skills/idd-issue/SKILL.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/issue-driven-dev/CHANGELOG.md b/plugins/issue-driven-dev/CHANGELOG.md index bf7e7a8..3cadb64 100644 --- a/plugins/issue-driven-dev/CHANGELOG.md +++ b/plugins/issue-driven-dev/CHANGELOG.md @@ -15,11 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Refactored -- **`spectra-archive` skill `.agents/` ↔ `.claude/` sync** ([#93](https://github.com/PsychQuant/issue-driven-development/issues/93)): #93 surfaced 3-copy divergence between `.claude/skills/`, `.agents/skills/`, and `plugins/.../references/spectra-skills/`. Investigation refuted the diagnose-time recommendation to delete `.agents/` — 4 openspec specs reference `.agents/skills/spectra-*/SKILL.md` as Spectra-tier dependencies (the path is LIVE, not legacy). Revised disposition: sync `.agents/skills/spectra-archive/SKILL.md` from `.claude/` so both LIVE load paths carry the v1.3+ Implementation Complete auto-post feature (#56). `plugins/.../references/spectra-skills/spectra-archive/` left as historical snapshot (no markdown cross-refs found; low cleanup ROI per `lead-minimal`). **Sister-skill divergences out of scope**: 7 other spectra-* skills (audit / discuss / propose / apply / ingest / debug / commit) also diverge between `.claude/` and `.agents/` — audit comment on #93 documents the drift matrix; each candidate filed for separate follow-up as needed. Drift-prevention CI hook deferred until drift recurs naturally. +- **`spectra-archive` skill `.agents/` ↔ `.claude/` sync** ([#93](https://github.com/PsychQuant/issue-driven-development/issues/93)): #93 surfaced 3-copy divergence between `.claude/skills/`, `.agents/skills/`, and `plugins/.../references/spectra-skills/`. Investigation refuted the diagnose-time recommendation to delete `.agents/` — 4 openspec specs reference `.agents/skills/spectra-*/SKILL.md` as Spectra-tier dependencies (the path is LIVE, not legacy). Revised disposition: sync `.agents/skills/spectra-archive/SKILL.md` from `.claude/` so both LIVE load paths carry the v1.3+ Implementation Complete auto-post feature (#56). `plugins/.../references/spectra-skills/spectra-archive/` left as historical snapshot (no markdown cross-refs found; low cleanup ROI per `lead-minimal`). **Sister-skill divergences out of scope**: 7 other spectra-* skills (audit / discuss / propose / apply / ingest / debug / commit) also diverge between `.claude/` and `.agents/` — audit comment on #93 documents the drift matrix. **Sister issues NOT auto-filed in this PR** per `feedback_lead_minimal` — drift documented as observation, separate issues will be filed if specific divergence causes user-visible friction. (Original wording "filed for separate follow-up as needed" was misleading per #115 DA finding DA-1 — no issues actually filed.) Drift-prevention CI hook deferred until drift recurs naturally. - **`idd-implement` cluster detection glob hardening + Option A-final doc** ([#100](https://github.com/PsychQuant/issue-driven-development/issues/100)): two non-blocking findings from PR #99 (#96) verify rounds. - **Finding 1 (design)** — Option A (cluster mode unconditionally forces PR regardless of branch context) confirmed final. NEW `### Feature-branch + cluster + direct-commit — rejected case` subsection in `references/pr-flow.md` § Cluster mode override documenting the rejected Option B (branch-context-gated cluster direct-commit) with comparison table + rationale. Contract simplicity wins; feature-branch direct-commit workflow remains viable for single-issue `--no-pr` invocations. - - **Finding 2 (refactor)** — `idd-implement` Step 0.5 cluster detection bash hardened. Previous glob `\#[0-9]*` over-matched (`#42abc` counted, `#34 #34` over-counted as 2). Replaced with strict integer check (`[[ "$arg_num" =~ ^[0-9]+$ ]]`) + associative-array dedup matching the documented `^#\d+$` form in `batch-and-cluster.md`. 0 behavior change for well-formed distinct invocations. + - **Finding 2 (refactor)** — `idd-implement` Step 0.5 cluster detection bash hardened. Previous glob `\#[0-9]*` over-matched (`#42abc` counted, `#34 #34` over-counted as 2). Replaced with strict integer check (`[[ "$arg_num" =~ ^[0-9]+$ ]]`) + associative-array dedup matching the documented `^#\d+$` form in `batch-and-cluster.md`. 0 behavior change for well-formed distinct invocations. **Quiet behavior change for malformed tokens** (per #115 DA finding DA-2): pre-v2.70.0, `#42abc` was counted as a cluster member (causing later failures when used as issue number); post-v2.70.0 it's silently skipped from the count. Users invoking with typo'd tokens get cluster-mode evaluation based on well-formed tokens only — failure modes shifted from "fail mid-loop on bad number" to "treat as if token not present". ### Notes diff --git a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md index 9ba470e..606a262 100644 --- a/plugins/issue-driven-dev/skills/idd-issue/SKILL.md +++ b/plugins/issue-driven-dev/skills/idd-issue/SKILL.md @@ -273,7 +273,7 @@ UPSTREAM=$(echo "$REPO_JSON" | jq -r '.parent.nameWithOwner // empty') | Telegram chat range | `mcp__plugin_che-telegram-mcp_telegram-all__get_chat_history(chat_id, limit)` 或 `dump_chat_to_markdown` | 列舉 chat 中所有 `[photo]` / `[document]` / `[video]` placeholder → 嘗試 MCP `download_file`(若存在)→ 否則**明列檔名 + 必要請求**讓使用者用 Telegram client 手動存檔到指定路徑後 skill 接手 upload | | Apple Mail / 郵件 | `mcp__plugin_che-apple-mail-mcp_mail__get_email(message_id)` | `list_attachments` → `save_attachment(filename, output_path)` | | Apple Notes | `mcp__plugin_che-apple-notes-mcp_notes__get_note` | 同上 export 全部 inline 圖 | -| Pasted image (`[Image: source: ~/.claude/image-cache/...]`) | n/a — image-only | **立即** `cp` 到 `/tmp/idd-issue-attachments/issue_pending__.png` 在 *讀到 annotation 的同一 tool turn* — see "Pasted-image immediate-persistence" below (v2.70.0+, #112) | +| Pasted image (`[Image: source: ~/.claude/image-cache/...]`) | n/a — image-only | **立即** `cp` 到 `/tmp/idd-issue-attachments/issue_pending_XXXXXX.png` (via `mktemp`) 在 *讀到 annotation 的同一 tool turn* — see "Pasted-image immediate-persistence" below (v2.70.0+, #112) | | 直接貼文字(無附件) | argument 直接帶文字 | n/a | | 混合(文字 + 圖片貼上) | argument 帶文字 + `[Image:...]` annotation | **每張 pasted image 都套用 Pasted-image immediate-persistence**;使用者額外提供的 file path 直接納入 Step 4 上傳清單 | @@ -283,7 +283,7 @@ UPSTREAM=$(echo "$REPO_JSON" | jq -r '.parent.nameWithOwner // empty') **Rule (SHALL)**: when Step 1 encounters a `[Image: source: ]` annotation in the prompt, **`cp` the image to a stable staging path within the SAME tool turn** that first sees the annotation. Do NOT defer to Step 4. The staged path joins Step 4's upload list; the original `~/.claude/image-cache/` path is no longer referenced after Step 1. -**PASTED_IMAGE_PATHS source contract**: when the agent (Claude Code) sees `[Image: source: ]` annotation(s) in the user's prompt (one or more), populate a bash array `PASTED_IMAGE_PATHS=( "/Users/che/.claude/image-cache//1.png" ... )` with one entry per annotation before invoking the staging loop below. The annotation is the **only** authoritative source — there is no separate enumeration API; the agent reads the prompt text and extracts annotation `source:` values verbatim. +**PASTED_IMAGE_PATHS source contract** (as of v2.70.0+): when the agent (Claude Code) sees `[Image: source: ]` annotation(s) in the user's prompt (one or more), populate a bash array `PASTED_IMAGE_PATHS=( "/Users/che/.claude/image-cache//1.png" ... )` with one entry per annotation before invoking the staging loop below. The annotation is currently the only authoritative source per the v2.70.0+ Claude Code prompt format — there is no separate enumeration API; the agent reads the prompt text and extracts annotation `source:` values verbatim. (If future Claude Code versions add drag-drop or other attachment annotation forms, this contract may need extension.) ```bash # Run in the SAME tool turn that first sees the [Image: source: ...] annotation.