From 0fdd2e0c195cb53c327a34f389b7f429cb998a11 Mon Sep 17 00:00:00 2001 From: che cheng Date: Mon, 27 Jul 2026 15:53:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(docs):=20Path=20Map=20wiki=20pipeline=20?= =?UTF-8?q?=E2=80=94=20flowchart=20+=20generator=20+=20drift-guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 of the #276 two-phase plan (Phase 1 = PR #279, merged). The user ruling: 'path 應該要做好,做成一個流程圖放在 wiki,不然我也會搞不清楚' — a daily-reference navigation map for the maintainer, not onboarding prose. docs/workflows.md gains a '## Path Flowchart (wiki source)' section: a mermaid flowchart whose spine faithfully mirrors the existing Path Selection Decision Tree (meeting gate -> complexity -> attended -> multi-shape) and whose legend subgraphs carry every off-spine family — all 36 catalog paths appear. New scripts/generate-path-map.py renders docs/wiki/Path-Map.md deterministically (mermaid lifted verbatim + per-category tables from the '#### P-' headings; no timestamps, so the guard can byte-diff). New drift-guard path-map-sync locks three invariants: freshness (generator --check), coverage (every P- name in the mermaid AND the page — a new path that skips the map goes red), discovery (README links the wiki page). README pipeline diagram gains the pointer line whose fix Phase 1 deliberately deferred until this page existed. Wiki sync copies docs/wiki/Path-Map.md verbatim. Sweep 42 suites 0 fail. Refs #276 --- docs/wiki/Path-Map.md | 149 ++++++++++++++++++ docs/workflows.md | 68 ++++++++ plugins/issue-driven-dev/README.md | 2 + .../scripts/tests/path-map-sync/test.sh | 68 ++++++++ scripts/generate-path-map.py | 114 ++++++++++++++ 5 files changed, 401 insertions(+) create mode 100644 docs/wiki/Path-Map.md create mode 100644 plugins/issue-driven-dev/scripts/tests/path-map-sync/test.sh create mode 100644 scripts/generate-path-map.py diff --git a/docs/wiki/Path-Map.md b/docs/wiki/Path-Map.md new file mode 100644 index 0000000..761f1ac --- /dev/null +++ b/docs/wiki/Path-Map.md @@ -0,0 +1,149 @@ +# IDD Path Map + +> **36 條 sanctioned path 的選擇導航圖** — maintainer 日常查詢用(#276)。 +> Single source:[docs/workflows.md](https://github.com/PsychQuant/issue-driven-development/blob/main/docs/workflows.md)(§ Path Flowchart + § Path Catalog); +> 本頁由 `scripts/generate-path-map.py` 生成,**請勿直接編輯** — 改 workflows.md 後重跑 generator。 + +```mermaid +flowchart TD + START(["你正要做什麼?"]) --> Q1{"single issue
還是 multi?"} + + Q1 -->|single| QM{"type=meeting?"} + QM -->|yes| A5["P-meeting
(複雜度評估前分流)"] + QM -->|no| Q2{"Complexity?
(硬閘:≥5 檔互依概念或
shared abstraction → MUST Plan)"} + Q2 -->|Simple| Q3{"attended?
(你在 keyboard)"} + Q3 -->|yes| A1["P-atomic(5 touchpoints)"] + Q3 -->|no| QD{"已有 diagnosis?"} + QD -->|yes| B1["P-auto-from-diagnosed"] + QD -->|no| B2["P-auto-full-swallow"] + Q2 -->|Plan| A2["P-plan-gated
(EnterPlanMode 審批)"] + Q2 -->|Spectra| Q4{"方向已明確?
(零 open questions)"} + Q4 -->|yes(罕用)| A4["P-spectra-opt-out"] + Q4 -->|no(預設)| A3["P-spectra-discuss-first"] + + Q1 -->|multi| Q5{"issues 之間的關係?"} + Q5 -->|同根 cluster| B4["P-batch-diagnose → P-cluster-pr"] + Q5 -->|ripple:1 root + auto-emerge| B5["P-chain-from-root"] + Q5 -->|forest:N roots 獨立| B6["P-chain-multi-root
(或 N × P-atomic)"] + Q5 -->|unrelated backlog drain| B3["P-batch-drain
(conflict-class ordered)"] + Q5 -->|source-driven(一份來源 ≥2 findings)| D1["P-multi-finding
(強制單線 → P-no-multi-finding)"] + + subgraph C["C. Batch(N issues 各自 atomic,無共用 PR)"] + C2["P-batch-comment"] + C3["P-batch-update"] + C4["P-batch-edit"] + C5["P-batch-close"] + end + + subgraph E["E. Verify-only / PR review"] + E1["P-pr-verify"] + E2["P-verify-file-profile(非 code deliverable)"] + E3["P-pr-verify-then-merge"] + end + + subgraph F["F. Resume / 中斷恢復"] + F1["P-spectra-resume"] + F2["P-implement-retry"] + end + + subgraph G["G. Non-lifecycle / ancillary"] + G1["P-list-triage"] + G2["P-route-recommend"] + G3["P-comment-only"] + G4["P-edit-only"] + G5["P-discussions-intake"] + G6["P-clarify-audit"] + G7["P-find-lookup"] + G8["P-ask-history"] + G9["P-report-rollup"] + G10["P-config-maintain"] + end + + subgraph H["H. Autopilot(no human-in-loop)"] + H1["P-loop-autopilot"] + H2["P-cron-autopilot"] + H3["P-cron-list-triage"] + end +``` + +## Path 一覽(依類別) + +### A. Single-issue lifecycle paths(typical bug / feature flow) + +| Path | 說明 | +|------|------| +| `P-atomic` | Simple complexity baseline | +| `P-plan-gated` | Plan tier with EnterPlanMode approval | +| `P-spectra-discuss-first` | Spectra tier with alignment | +| `P-spectra-opt-out` | Spectra with direction pre-confirmed(罕用 | +| `P-meeting` | meeting-type deliberation(#57,v2.93+ | + +### B. Convenience-orchestrator paths(快速 lifecycle 完成) + +| Path | 說明 | +|------|------| +| `P-auto-from-diagnosed` | diagnose 已過,跑自動化 | +| `P-auto-full-swallow` | legacy `idd-all` 一次跑完 | +| `P-batch-drain` | multi-issue conflict-class-ordered sequential(v2.83+,#182 | +| `P-cluster-pr` | N issues 共 1 PR | +| `P-chain-from-root` | 1 root + auto-emergent spawn | +| `P-chain-multi-root` | 多 root forest chain | + +### C. Batch paths(N issues 各自 atomic) + +| Path | 說明 | +|------|------| +| `P-batch-diagnose` | - **Use case**:同 doc 來源 N issues,各自 diagnose 各 user-reviewable | +| `P-batch-comment` | - **Use case**:批次加同一段 note(e.g.「blocked by upstream」)到多 issue | +| `P-batch-update` | - **Use case**:批次同步 Current Status phase | +| `P-batch-edit` | - **Use case**:批次套同一段 edit 到多 issue 既存 comment | +| `P-batch-close` | - **Use case**:cluster PR merge 後批次 close,**每個 issue 仍各自獨立 closing summary** | + +### D. Multi-finding dispatch paths(source-driven mixed routing) + +| Path | 說明 | +|------|------| +| `P-multi-finding` | auto-triggered | +| `P-no-multi-finding` | force single-issue | + +### E. Verify-only / PR-review paths(external agent integration) + +| Path | 說明 | +|------|------| +| `P-pr-verify` | - **Use case**:外部 agent(Codex / Copilot)開的 PR,IDD 6-AI ensemble verify | +| `P-verify-file-profile` | 非 code deliverable 驗證(#258,v2.97+ | +| `P-pr-verify-then-merge` | - **Use case**:verify-gated PASS 後 user 主動 merge,**per IDD MANIFESTO**「auto-merge 須走 #3… | + +### F. Resume / continuation paths(mid-work recovery) + +| Path | 說明 | +|------|------| +| `P-spectra-resume` | - **Use case**:Spectra change 中途 requirements 變更,重新對齊 spec 後繼續 | +| `P-implement-retry` | - **Use case**:Chain mode 某 issue verify FAIL,retry on cluster branch(避免另開新 branch) | + +### G. Non-lifecycle / ancillary paths(maintenance + observation) + +| Path | 說明 | +|------|------| +| `P-list-triage` | - **Use case**:開工前 triage,看哪些 open issue,各自 phase 為何 | +| `P-route-recommend` | - **Use case**:選 agent(Codex / Claude / etc.) | +| `P-comment-only` | 非 lifecycle 推進 | +| `P-edit-only` | - **Use case**:編輯既存 comment(append / replace / prepend-note) | +| `P-discussions-intake` | Discussions 盲點橋接(#221,v2.95+ | +| `P-clarify-audit` | terminology / semantic surfacing(#135,v2.72+ | +| `P-find-lookup` | 語意查找(#139,v2.97+ | +| `P-ask-history` | issue 知識庫問答(#72,v2.99+ | +| `P-report-rollup` | 跨 issue 人類 triage 視圖(#134,v2.97+ | +| `P-config-maintain` | config 生命週期(v2.31+ | + +### H. Unsupervised / autopilot paths(no human-in-loop) + +| Path | 說明 | +|------|------| +| `P-loop-autopilot` | - **Use case**:autonomous 持續執行;適合 well-bounded simple issues | +| `P-cron-autopilot` | - **Use case**:cron-scheduled ETL / housekeeping | +| `P-cron-list-triage` | - **Use case**:每週一 9am 印出 open issue triage report | + +--- + +細節(touchpoints、mode、assumptions、risk)與 § Anti-patterns 見 [docs/workflows.md](https://github.com/PsychQuant/issue-driven-development/blob/main/docs/workflows.md)。 diff --git a/docs/workflows.md b/docs/workflows.md index 9ad1556..443d261 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -427,6 +427,74 @@ idd-edit comment:NNN --append --body "..." --- +## Path Flowchart(wiki source,#276 Phase 2) + +> 本節的 mermaid 是 **wiki [Path Map](https://github.com/PsychQuant/issue-driven-development/wiki/Path-Map) 頁的 single source** — `scripts/generate-path-map.py` 把它與上方 catalog 一起渲染成 `docs/wiki/Path-Map.md`,wiki 同步逐字複製該檔。改這裡,跑 generator,別直接改 wiki。drift-guard:`path-map-sync`(freshness + 36 條 path 全覆蓋 + README 連結)。主幹忠實鏡像 § Path Selection Decision Tree;不在主幹上的 family 以 legend 群組列出。 + +```mermaid +flowchart TD + START(["你正要做什麼?"]) --> Q1{"single issue
還是 multi?"} + + Q1 -->|single| QM{"type=meeting?"} + QM -->|yes| A5["P-meeting
(複雜度評估前分流)"] + QM -->|no| Q2{"Complexity?
(硬閘:≥5 檔互依概念或
shared abstraction → MUST Plan)"} + Q2 -->|Simple| Q3{"attended?
(你在 keyboard)"} + Q3 -->|yes| A1["P-atomic(5 touchpoints)"] + Q3 -->|no| QD{"已有 diagnosis?"} + QD -->|yes| B1["P-auto-from-diagnosed"] + QD -->|no| B2["P-auto-full-swallow"] + Q2 -->|Plan| A2["P-plan-gated
(EnterPlanMode 審批)"] + Q2 -->|Spectra| Q4{"方向已明確?
(零 open questions)"} + Q4 -->|yes(罕用)| A4["P-spectra-opt-out"] + Q4 -->|no(預設)| A3["P-spectra-discuss-first"] + + Q1 -->|multi| Q5{"issues 之間的關係?"} + Q5 -->|同根 cluster| B4["P-batch-diagnose → P-cluster-pr"] + Q5 -->|ripple:1 root + auto-emerge| B5["P-chain-from-root"] + Q5 -->|forest:N roots 獨立| B6["P-chain-multi-root
(或 N × P-atomic)"] + Q5 -->|unrelated backlog drain| B3["P-batch-drain
(conflict-class ordered)"] + Q5 -->|source-driven(一份來源 ≥2 findings)| D1["P-multi-finding
(強制單線 → P-no-multi-finding)"] + + subgraph C["C. Batch(N issues 各自 atomic,無共用 PR)"] + C2["P-batch-comment"] + C3["P-batch-update"] + C4["P-batch-edit"] + C5["P-batch-close"] + end + + subgraph E["E. Verify-only / PR review"] + E1["P-pr-verify"] + E2["P-verify-file-profile(非 code deliverable)"] + E3["P-pr-verify-then-merge"] + end + + subgraph F["F. Resume / 中斷恢復"] + F1["P-spectra-resume"] + F2["P-implement-retry"] + end + + subgraph G["G. Non-lifecycle / ancillary"] + G1["P-list-triage"] + G2["P-route-recommend"] + G3["P-comment-only"] + G4["P-edit-only"] + G5["P-discussions-intake"] + G6["P-clarify-audit"] + G7["P-find-lookup"] + G8["P-ask-history"] + G9["P-report-rollup"] + G10["P-config-maintain"] + end + + subgraph H["H. Autopilot(no human-in-loop)"] + H1["P-loop-autopilot"] + H2["P-cron-autopilot"] + H3["P-cron-list-triage"] + end +``` + +--- + ## Path × Skill Matrix | Skill | A:atomic | A:plan | A:spectra | B:auto | B:cluster | B:chain | C:batch | D:multi-finding | E:pr-verify | F:resume | G:non-lifecycle | H:autopilot | diff --git a/plugins/issue-driven-dev/README.md b/plugins/issue-driven-dev/README.md index 69950a0..41f24e7 100644 --- a/plugins/issue-driven-dev/README.md +++ b/plugins/issue-driven-dev/README.md @@ -53,6 +53,8 @@ idd-issue → idd-diagnose → idd-implement → idd-verify → idd-close ① ② ③ ④ ⑤ ``` +> 上圖是最常見的 P-atomic 主幹。完整 **36 條 sanctioned path** 的選擇導航(何時走 Plan / Spectra / cluster / chain / batch…)見 wiki **[Path Map](https://github.com/PsychQuant/issue-driven-development/wiki/Path-Map)**(流程圖 + 分類表;source = `docs/workflows.md`,generator 防 drift,#276)。 + | Skill | Purpose | |-------|---------| | `idd-issue` | Create well-documented GitHub Issue with original quotes and images | diff --git a/plugins/issue-driven-dev/scripts/tests/path-map-sync/test.sh b/plugins/issue-driven-dev/scripts/tests/path-map-sync/test.sh new file mode 100644 index 0000000..e274ea1 --- /dev/null +++ b/plugins/issue-driven-dev/scripts/tests/path-map-sync/test.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# test.sh — drift-guard for the Path Map wiki pipeline (#276 Phase 2). +# +# CONTRACT: docs/workflows.md is the single source (its `## Path Flowchart` +# mermaid block + the `#### P-` catalog); scripts/generate-path-map.py renders +# the committed wiki-source page docs/wiki/Path-Map.md deterministically; the +# wiki sync copies that file verbatim. Three invariants keep the navigation +# map from silently rotting (the #122/#267 drift class, third instance): +# 1. FRESHNESS — regenerating must reproduce the committed page byte-for-byte +# 2. COVERAGE — every P- name in the catalog appears in the mermaid block +# AND in the generated page (a new path that skips the map +# goes red here, not unnoticed) +# 3. DISCOVERY — README points at the wiki page +# +# Usage: bash test.sh (exit 0 = all pass, 1 = any fail) + +set -u + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PLUGIN_ROOT="$HERE/../../.." +REPO_ROOT="$PLUGIN_ROOT/../.." +WORKFLOWS="$REPO_ROOT/docs/workflows.md" +GEN="$REPO_ROOT/scripts/generate-path-map.py" +PAGE="$REPO_ROOT/docs/wiki/Path-Map.md" +README="$PLUGIN_ROOT/README.md" + +HELPERS="$HERE/../../lib/assert-helpers.sh" +[ -f "$HELPERS" ] || { echo "✗ missing $HELPERS — cannot run suite" >&2; exit 1; } +. "$HELPERS" + +assert_file_exists "docs/workflows.md exists" "$WORKFLOWS" +assert_file_exists "generator exists" "$GEN" +assert_file_exists "committed wiki-source page exists" "$PAGE" + +# ── source section present ── +assert_output_grep "workflows.md has the Path Flowchart section" "## Path Flowchart" "$WORKFLOWS" +assert_output_grep "flowchart section carries a mermaid fence" '```mermaid' "$WORKFLOWS" + +# ── invariant 1: freshness (generator --check diffs against committed page) ── +OUT=$(python3 "$GEN" --check 2>&1); RC=$? +if [ "$RC" -eq 0 ]; then + pass "Path-Map.md is fresh (generator --check clean)" +else + fail "Path-Map.md is fresh (generator --check clean)" "$OUT" +fi + +# ── invariant 2: coverage — every catalog P- name in mermaid block AND page ── +MERMAID=$(awk '/^## Path Flowchart/{f=1} f && /^```mermaid/{m=1; next} m && /^```/{exit} m' "$WORKFLOWS") +MISS_M=""; MISS_P="" +while IFS= read -r NAME; do + case "$MERMAID" in *"$NAME"*) : ;; *) MISS_M="$MISS_M $NAME" ;; esac + grep -qF -- "$NAME" "$PAGE" || MISS_P="$MISS_P $NAME" +done < <(grep -oE '^#### (P-[a-z0-9-]+)' "$WORKFLOWS" | sed 's/^#### //' | sort -u) +if [ -z "$MISS_M" ]; then + pass "every catalog path appears in the mermaid flowchart" +else + fail "every catalog path appears in the mermaid flowchart" "missing:$MISS_M" +fi +if [ -z "$MISS_P" ]; then + pass "every catalog path appears in Path-Map.md" +else + fail "every catalog path appears in Path-Map.md" "missing:$MISS_P" +fi + +# ── invariant 3: discovery ── +assert_output_grep "README links to the wiki Path Map" "wiki/Path-Map" "$README" + +print_summary "path-map-sync" diff --git a/scripts/generate-path-map.py b/scripts/generate-path-map.py new file mode 100644 index 0000000..31948a9 --- /dev/null +++ b/scripts/generate-path-map.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""generate-path-map.py — render docs/wiki/Path-Map.md from docs/workflows.md (#276 Phase 2). + +Single source: docs/workflows.md — its `## Path Flowchart` mermaid block is +lifted verbatim, and the `### .` / `#### P-` catalog headings become +per-category navigation tables. Output is DETERMINISTIC (no timestamps) so the +path-map-sync drift-guard can byte-diff the committed page. + +Usage: + python3 scripts/generate-path-map.py # write docs/wiki/Path-Map.md + python3 scripts/generate-path-map.py --check # exit 1 if committed page is stale +""" +import re +import sys +import pathlib + +ROOT = pathlib.Path(__file__).resolve().parent.parent +WORKFLOWS = ROOT / "docs" / "workflows.md" +PAGE = ROOT / "docs" / "wiki" / "Path-Map.md" + +REPO_BLOB = "https://github.com/PsychQuant/issue-driven-development/blob/main/docs/workflows.md" + + +def extract_mermaid(text: str) -> str: + """The mermaid fence inside `## Path Flowchart` — verbatim.""" + m = re.search(r"^## Path Flowchart.*?^```mermaid\n(.*?)^```", text, re.M | re.S) + if not m: + sys.exit("✗ workflows.md has no `## Path Flowchart` mermaid block") + return m.group(1).rstrip("\n") + + +def extract_catalog(text: str): + """[(category_heading, [(p_name, tail_or_first_line), ...]), ...]""" + cats = [] + cur = None + lines = text.splitlines() + i = 0 + while i < len(lines): + line = lines[i] + cm = re.match(r"^### ([A-H]\..+)$", line) + if cm: + cur = (cm.group(1).strip(), []) + cats.append(cur) + pm = re.match(r"^#### (P-[a-z0-9-]+)\s*(?:[—(](.*))?$", line) + if pm and cur is not None: + name = pm.group(1) + tail = (pm.group(2) or "").strip().rstrip("))").strip() + if not tail: + # fall back to the section's first prose line + j = i + 1 + while j < len(lines) and (not lines[j].strip() or lines[j].startswith("```")): + # skip blanks; skip a whole fence if one opens + if lines[j].startswith("```"): + j += 1 + while j < len(lines) and not lines[j].startswith("```"): + j += 1 + j += 1 + if j < len(lines): + tail = lines[j].strip() + if len(tail) > 90: + tail = tail[:87] + "…" + cur[1].append((name, tail)) + i += 1 + return [c for c in cats if c[1]] + + +def render(text: str) -> str: + mermaid = extract_mermaid(text) + cats = extract_catalog(text) + n = sum(len(ps) for _, ps in cats) + out = [] + out.append("# IDD Path Map") + out.append("") + out.append(f"> **{n} 條 sanctioned path 的選擇導航圖** — maintainer 日常查詢用(#276)。") + out.append(f"> Single source:[docs/workflows.md]({REPO_BLOB})(§ Path Flowchart + § Path Catalog);") + out.append("> 本頁由 `scripts/generate-path-map.py` 生成,**請勿直接編輯** — 改 workflows.md 後重跑 generator。") + out.append("") + out.append("```mermaid") + out.append(mermaid) + out.append("```") + out.append("") + out.append("## Path 一覽(依類別)") + out.append("") + for heading, paths in cats: + out.append(f"### {heading}") + out.append("") + out.append("| Path | 說明 |") + out.append("|------|------|") + for name, tail in paths: + out.append(f"| `{name}` | {tail} |") + out.append("") + out.append("---") + out.append("") + out.append(f"細節(touchpoints、mode、assumptions、risk)與 § Anti-patterns 見 [docs/workflows.md]({REPO_BLOB})。") + return "\n".join(out) + "\n" + + +def main() -> None: + text = WORKFLOWS.read_text(encoding="utf-8") + rendered = render(text) + if "--check" in sys.argv: + if not PAGE.exists(): + sys.exit(f"✗ {PAGE} missing — run the generator") + if PAGE.read_text(encoding="utf-8") != rendered: + sys.exit(f"✗ {PAGE} is STALE — workflows.md changed; rerun: python3 scripts/generate-path-map.py") + print("OK — Path-Map.md is fresh") + return + PAGE.parent.mkdir(parents=True, exist_ok=True) + PAGE.write_text(rendered, encoding="utf-8") + print(f"wrote {PAGE} ({n if (n := rendered.count('| `P-')) else 0} paths)") + + +if __name__ == "__main__": + main()