Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d17d803
module_d: initial PLAN with scope, interfaces, pending grill items
RiceTooCold May 29, 2026
4f76b8a
module_d: D-1/D-2 probe — practice extraction + co-association consen…
RiceTooCold May 29, 2026
e0b0afd
docs: sync Module-D probe decisions — ADR 0006/0007 + spec/CONTEXT/RE…
RiceTooCold May 29, 2026
001f28f
module_d: D-5 viz idiom probes — organic force bubbles + click-trajec…
RiceTooCold May 30, 2026
e497782
module_d: real module with B/C-decoupled contract + dashboard (proxy-…
RiceTooCold Jun 1, 2026
fecf3fe
module_d/dashboard: community-swimlane timeline + inline/push detail …
RiceTooCold Jun 1, 2026
f8651b4
docs: ADR 0008 (D↔B/C data contract / provider seams) + glossary updates
RiceTooCold Jun 1, 2026
8397603
docs: dashboard visual design spec (Editorial · Ink) + ignore brainst…
RiceTooCold Jun 1, 2026
3b1e326
feat(module_d): Editorial·Ink dashboard reskin + practice-led answer …
RiceTooCold Jun 1, 2026
f04f612
feat(module_d): render SO markdown answer body (prose + code blocks)
RiceTooCold Jun 1, 2026
96fc4df
docs(spec): correct + complete the Editorial·Ink design record
RiceTooCold Jun 1, 2026
61e7d03
probe(d1): full 163 extraction — 192 practices, prose-dominant drift
RiceTooCold Jun 1, 2026
777b3cd
feat(d2): full-scale k=3 consensus clustering → real-data dashboard
RiceTooCold Jun 1, 2026
0b6fc87
probe(d2c): head-only ARI diagnostic — the 0.255 drop is a granularit…
RiceTooCold Jun 1, 2026
6ab4409
docs(plan): record head-cluster content-validity review (dup-chain pr…
RiceTooCold Jun 1, 2026
5dc2b5c
docs(plan): record practice-level relevance principle + score-floor d…
RiceTooCold Jun 1, 2026
1a17f69
feat(d1b): practice-level relevance gate -> clean 13-cluster landscape
RiceTooCold Jun 1, 2026
a67c22b
feat(d1g): merge extract+gate into one call; deterministic evidence_type
RiceTooCold Jun 2, 2026
a40845c
feat(d4): per-query narrative — RQ2/3/4 shape description as prose
RiceTooCold Jun 2, 2026
55b6949
feat(d2): re-cluster onto merged 163 landscape; promote + rebuild das…
RiceTooCold Jun 2, 2026
de64cc3
refactor(d): realign artifacts to merged d1_gated pipeline; re-verify…
RiceTooCold Jun 2, 2026
bddb32c
feat(d4): wire narrative onto dashboard (overture → standfirst ribbon)
RiceTooCold Jun 2, 2026
0df5e31
chore(d): prune superseded probes + regeneratable artifacts (pre-merge)
RiceTooCold Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
# 註冊:https://stackapps.com/apps/oauth/register
# 複製此檔為 .env 並填入 key(.env 已 gitignored)
SE_API_KEY=

# OpenAI API key — Module D LLM roles (gate / extraction / aggregator / narrative)
# Provider 走 provider-neutral seam,預設 OpenAI(ADR 0002 / PLAN ▼Q2)
OPENAI_API_KEY=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ data/*

# OS
.DS_Store

# Brainstorming visual companion (local mockups)
.superpowers/
14 changes: 8 additions & 6 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Language

**Query**:
一段 developer-facing 自然語言問題,研究的測量入口。Demo 預備一組 query 作為 finding 載體。
一段 developer-facing 自然語言問題,研究的測量入口。**是開放的實作問題(「這問題有哪些實作解法?」),不預設已命名的 X-vs-Y 選擇;「選擇」從 practice breakdown emergent(ADR 0006)。** Demo 預備一組 query 作為 finding 載體。
_Avoid_: question(保留給 SO post 那種),prompt(保留給 LLM input)

**Question**:
Expand All @@ -17,16 +17,16 @@ _Avoid_: query,post(太泛)
_Avoid_: topic(已棄用 BERTopic 框架),cluster(保留給 practice 側)

**Answer**:
SO 上一篇 answer post(body)。Raw embedding clustering 在 answer body 上 probe 已證為失敗路線。
SO 上一篇 answer post(body)。Raw embedding clustering 在 answer body 上 probe 已證為失敗路線。Dashboard 上標為 **"original answer"**,與我們抽出的 **practice** 做明確區分(answer = SO 原文,practice = 我們抽的單位)。
_Avoid_: response, comment

**Practice**:
從一篇 answer 用 LLM 抽出的結構化 normalization:`{practice: 1 sentence, conditions: short list, evidence_type: prose|code|both}`。**Answer 側的 normalization 單位**,分群在 practice 上做、不在 raw answer 上做。Step 1 of hierarchical map-reduce (ADR 0002)。
從一篇 answer 用 LLM 抽出的結構化 normalization:`{practice: 1 sentence, conditions: short list, evidence_type: prose|code|both}`。**Answer 側的 normalization 單位**,分群在 practice 上做、不在 raw answer 上做。Step 1 of hierarchical map-reduce (ADR 0002)。**一篇 answer 可抽出多個 practice(每個是一票;ADR 0007)。practice 專指推薦的實作 action(prescription);純解釋成因(diagnosis)的答案不產 practice(ADR 0006)。**
_Avoid_: stance(opinion-mining 借詞、不擬合 SO Q&A 的「推薦 action」結構,且和 project README 「community-consensus practice」標題不一致),opinion,view,claim

**Practice cluster**:
在一個 query-equivalent canonical group 內,多個 answer 的 practice 句聚成的群。**Consensus shape 的呈現單位**。Discovery 用 hierarchical map-reduce (ADR 0002):Step 2 LLM aggregator + companion deterministic SBERT+HDBSCAN run,兩者 agreement 報為 method-defensibility 指標。
_Avoid_: position cluster, answer cluster, stance cluster
在一個 query-equivalent canonical group 內,多個 answer 的 practice 句聚成的群。**Consensus shape 的呈現單位**。Discovery 用 hierarchical map-reduce (ADR 0002):Step 2 LLM aggregator + companion deterministic SBERT+HDBSCAN run,兩者 agreement 報為 method-defensibility 指標。Dashboard 上以 **"community"** 呈現(一群提供相近 practice 的 answers)—— 注意這是「做法社群」,**勿與整個 reactjs SO「社群/community」(專案標題的那個 community) 混淆**。
_Avoid_: position cluster, answer cluster, stance cluster;UI 的 "community" 不指 SO 全體群眾

**Breakdown / Shape**:
一個 canonical group 內各 practice cluster 的 size + authority share 分布。**取代 Convergent/Divergent hard label**;finding 是「shape 長這樣」,不是「label 是 X」。
Expand All @@ -44,8 +44,9 @@ _Avoid_: reputation alone(單純 SO native reputation 不是 authority signal
- A **Query** retrieves one **Query-equivalent canonical group**
- A **Query-equivalent canonical group** contains many **Questions**
- A **Question** has one or more **Answers**
- An **Answer** is normalized to one **Practice**(`{practice, conditions, evidence_type}`)
- An **Answer** is normalized to **one or more Practices**(每個 `{practice, conditions, evidence_type}`;ADR 0007
- **Practices** within a canonical group cluster into multiple **Practice clusters**
- 歸屬單位是 **Practice**,不是 Answer:一篇 Answer 的多個 Practice 可落在**不同 Practice cluster(community)**,故 Answer 沒有單一「所屬 cluster」
- Each **Practice cluster** has size + **Authority** share → forms the **Breakdown**

## Example dialogue
Expand All @@ -61,3 +62,4 @@ _Avoid_: reputation alone(單純 SO native reputation 不是 authority signal
- 早期討論「question 的聚合」混用了「BERTopic topic」與「canonical group」兩個概念 — 已釐清:BERTopic 路線棄用(ADR 0001),測量單位是 query-equivalent canonical group。
- 「Consensus」的判定方式:曾在「Convergent/Divergent hard label with 0.6 threshold」與「shape-as-finding」之間 — 已釐清:取後者(ADR 0001)。
- Answer 側的 normalization 單位曾叫 `stance` — 已改名為 `practice`(2026-05-29)。理由:`practice` 是 project 原生詞(README 標題就是 "community-consensus practice");`stance` 是 opinion-mining 借詞、預設「對固定 target claim 支持/反對」的二分結構,不擬合 SO Q&A 的「每個 answer 自行提議一個 action / approach」結構。
- **Dashboard 目前的 authority 與 grouping 是 SO proxy,非真 B/C**:authority = SO vote score(pre-differentiator、age-confounded),grouping = SO 原生 duplicate links(非真 canonical RAG+gate)。兩者藏在 `providers/so_proxy.py` 的 provider seam 後、可無痛替換(ADR 0008);UI 帶 provenance/PRE-DIFFERENTIATOR 標記,**勿把 proxy 數字當 finding**。
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Mapping community-consensus practice landscape in framework-level Stack Overflow

Stack Overflow 上「一個 question 多個 answer」的結構,本身就是 collective expert opinion 的 implicit aggregator。不同 answer 間的 practice 分布加上作者社群權威,能定量描述某社群對某類技術議題的共識狀態。

**2026-05-29 pivot 後**(見 `docs/adr/0001`–`0005`):對 developer query 採 LLM-canonicalized retrieval + hierarchical map-reduce practice extraction + community-network-derived authority overlay,揭示 React 社群在 implementation choice 場景下的 **practice breakdown shape**(emergent convergent / divergent / shifting / authority-disputed);不再有 hard typology label,shape 從 breakdown 自然 emergent。Demo = 6–8 stratified-selected developer queries。在資料覆蓋充足的 query 上做 per-query trajectory case study。
**2026-05-29 pivot 後**(見 `docs/adr/0001`–`0007`):對 developer query 採 LLM-canonicalized retrieval + hierarchical map-reduce practice extraction + community-network-derived authority overlay,揭示 React 社群在 implementation choice 場景下的 **practice breakdown shape**(emergent convergent / divergent / shifting / authority-disputed);不再有 hard typology label,shape 從 breakdown 自然 emergent。Demo = 6–8 stratified-selected developer queries。在資料覆蓋充足的 query 上做 per-query trajectory case study。

完整研究設計見:
- **`docs/spec.md`** — team-internal implementation reference(流程、演算法、DB schema、實作待辦)— **post-pivot 已對齊** ADR 0001–0005
Expand Down Expand Up @@ -52,7 +52,9 @@ cp .env.example .env
│ │ ├── 0002-practice-clustering-hierarchical-map-reduce.md
│ │ ├── 0003-authority-as-deterministic-weight-plus-narrative-overlay.md
│ │ ├── 0004-default-view-beeswarm-with-overlay.md
│ │ └── 0005-demo-query-selection-stratified-hybrid.md
│ │ ├── 0005-demo-query-selection-stratified-hybrid.md
│ │ ├── 0006-query-framing-open-implementation-problem.md
│ │ └── 0007-extraction-multipractice-clustering-method.md
│ └── reference/
│ └── se-api-overview.html # SE API reference
├── probe/ # 探索性檢驗
Expand Down
6 changes: 3 additions & 3 deletions docs/adr/0002-practice-clustering-hierarchical-map-reduce.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 0002 — Practice clustering via hierarchical map-reduce (LLM + companion deterministic run)

**Status:** accepted (2026-05-29, terminology updated practice/stance 2026-05-29)
**Status:** accepted (2026-05-29, terminology updated practice/stance 2026-05-29). **Refined by ADR 0007 (2026-05-29):** Step-1 emits a *list* of practices per answer; companion = matched-k agglomerative (not HDBSCAN-from-scratch); `majority_vote_on_assignments` = k=3 co-association consensus. Model/provider now OpenAI GPT-5.4 family (PLAN ▼Q2), not Haiku.

## Context

Expand All @@ -20,7 +20,7 @@ Independent WebSearch synthesis (2024–2026 literature) ratified the hierarchic

**Primary method:** Hierarchical map-reduce on practice statements.

- **Step 1 — Practice extraction (parallel, per answer)**: LLM extracts structured normalization from each answer. Output schema: `{practice: 1 sentence, conditions: short list, evidence_type: prose|code|both}`. The 1-sentence practice is the unit; the conditions field exists to prevent minority-position erasure (e.g., "recommends X *only if* state is local"). Few-shot React-specific anchoring; explicit instruction to interpret code blocks as practice evidence; temp=0.
- **Step 1 — Practice extraction (parallel, per answer)**: LLM extracts structured normalization from each answer. Output schema: `{practice: 1 sentence, conditions: short list, evidence_type: prose|code|both}`. The 1-sentence practice is the unit; the conditions field records scope caveats the answer **explicitly states** (e.g., "recommends X *only if* state is local"). Few-shot React-specific anchoring; explicit instruction to interpret code blocks as practice evidence; temp=0. **[Refined by ADR 0007: Step-1 emits a *list* of practices per answer (one answer can vote for several approaches); `conditions` is a displayed annotation that does NOT affect cluster boundaries.]**

- **Step 2 — Practice aggregator (one-shot)**: LLM aggregator over N short practice statements (≤2K tokens for N≤100; far below lost-in-the-middle danger zone). Output: cluster assignment per answer + cluster names + brief breakdown description, JSON-schema-enforced. Temp=0 + **k=3 multi-sample voting** on cluster assignments.

Expand All @@ -38,7 +38,7 @@ Independent WebSearch synthesis (2024–2026 literature) ratified the hierarchic

## Consequences

- Per-query cost ≈ $0.05 (Haiku 4.5 + parallel Step 1 + single Step 2); demo 8 queries total ≈ $0.40 / ~1 min wall clock. Not a constraint.
- Per-query cost (OpenAI GPT-5.4 family, measured in Module-D probe): extraction k=1 over ~163 pooled answers ≈ $0.21 (gpt-5.4-mini); aggregator k=3 one-shot ≈ $0.04 (gpt-5.4); narrative negligible. Demo 8 queries ≈ a few $. Not a constraint. (Old ~$0.05 estimate was bound to Haiku 4.5 + N=50.)
- LLM non-determinism is contained in two well-defined steps with explicit reproducibility controls (temp=0, structured output, k=3 voting). Step-1 schema (practice + conditions + evidence_type) prevents minority erasure.
- Companion deterministic run buys: (a) a reproducibility floor that does not depend on LLM availability, (b) a quantitative agreement number for the methodology section, (c) protection against silent LLM drift across model versions.
- Practice extraction has its own failure modes (information loss, code interpretation, domain jargon); these go in limitations with prompt-engineering mitigations recorded in implementation.
Expand Down
37 changes: 36 additions & 1 deletion docs/adr/0004-default-view-beeswarm-with-overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ADR 0003 鬆綁了 cluster authority 的單一公式,把 dashboard 重新定

Default view = **beeswarm**:

- **個別 answer = 1 點**(first-class visual unit)
- **個別 answer = 1 點**(first-class visual unit)—— 注:ADR 0007 後一答可含多 practice;default view 仍以該答案的 **primary** practice 一答一點,「一答多點」viz 延後(D-5 再定)
- **點水平位置** = practice cluster 分欄(cluster boundary 由 ADR 0002 決定)
- **點大小** = author authority(default yearly PageRank;可切 reputation / full-period PageRank / vote score / answer length 等)
- **點顏色** = year band(default;可切 accept_status / evidence_type / cluster_id 等)
Expand All @@ -30,3 +30,38 @@ Default view = **beeswarm**:
- **失去 of**:原 spec § 3.1 的 "tag-level landscape heatmap"(30 topic × 7 year 整面 heatmap)—— 那是 corpus-wide framing 的產物,ADR 0001 已棄
- **實作風險**:beeswarm with size / color 多 axis switching 在 Streamlit 上要 Plotly + 自訂 layout,不是 one-liner。Demo 8 query 可接受
- **可訪問性**:點大小編碼對視障使用者不友善,需要 alt text / sortable table fallback(保留 LineUp-style table 為 secondary tab,per 候選 D 的精神)

## Amendment (2026-05-30) — D-5 interactive viz exploration

Probe `src/module_d/probe_d/viz/` explored the default-view idiom space on real data (q54069253
slice, 46 practices). The tentative beeswarm default is refined into a small composed system:

- **Overview / default = clustered circle-pack**, realized as a **d3-force floating layout** with
**organic gooey/metaball cluster blobs** — the cluster boundary is an irregular shape that grows
along its member nodes, not a circle. Nodes draggable (fling); whole bubble draggable; collide
keeps nodes non-overlapping. (`viz/make_force.py` → `force.html`)
- **Single-practice temporal = click a practice → 2D continuous-time trajectory**: that cluster's
answers pin onto a real **date axis (old→new)**, beeswarm-stacked, others fade. This is the
PRIMARY analytical temporal tool. A single-practice trajectory is just a 1-D slice through the
space-time cube, so the cube is **not** needed for the core finding. (`viz/make_traj.py`; also
folded into `force.html`)
- **Time = expandable dimension, demoted from colour.** Ordered variable → ordered channel
(position / facet), never colour. Default is time-collapsed (pooled); time is revealed on demand
(per-practice trajectory, or per-year small-multiples). **This drops the original "point colour =
year band" default (§ 2.9)**; colour is freed (→ evidence_type / neutral).
- **3D Space-Time Cube = optional demo showpiece ONLY** (engagement, not a quantitative figure):
orthographic keeps cross-year sizes fair; empty gap-years shown honestly; gated on the 2019–2022
backfill. (`viz/make_stc_cube.py`; static concept = `data/d5_stc_concept.png`)
- **Static finding figure** stays beeswarm / circle-pack PNG (paper-printable).

Shared encoding (all idioms): **disk size ∝ log1p(vote)** (raw vote is power-law and can be
negative; size carries only ordinal "clearly bigger" — the exact authority/concentration number
goes in narrative/tooltip text); **accepted = ring**; **long-tail singletons collapse into one
labelled group** (not erased — long-tail mass is itself the divergent-shape signal). Authority here
is the **SO vote proxy — PRE-DIFFERENTIATOR and age-confounded** (older answers accrue votes); real
network authority (yearly PageRank) enters only with Module B (+ backfill for this fixture).

Tech: prototypes use matplotlib (static PNG) + D3/Three.js (interactive HTML). The production
dashboard leans **D3 / SVG** (force + trajectory) over the originally-assumed Streamlit + Plotly,
and the architecture stays open to a JS/WebGL stack (D3 / Three.js / deck.gl) for the cube
showpiece. Layout / animation polish is deferred (tune later).
2 changes: 1 addition & 1 deletion docs/adr/0005-demo-query-selection-stratified-hybrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Candidate strategies:

Adopt **stratified hybrid (a)+(c)**:pre-state criteria → 跑 candidate pool → data-driven 篩入 final N。

1. **Persona definition (pre-stated)**: "Stuck developer facing implementation choice — has a real implementation problem, doesn't know how to implement or which of several approaches to use." 這個 persona 自然 implies 多種 approach 存在,是 community consensus measurement 的有效場景。
1. **Persona definition (pre-stated)**: "Stuck developer facing implementation choice — has a real implementation problem, doesn't know how to implement or which of several approaches to use." 這個 persona 自然 implies 多種 approach 存在,是 community consensus measurement 的有效場景。**(Broadened by ADR 0006:query 是開放實作問題「這問題有哪些實作解法」、不預設已命名的 X-vs-Y choice;此 implementation-choice persona 為其特例。Stratification machinery 不變。)**

2. **Stratification dimensions (pre-stated)**:
- **Shape stratum** (predicted breakdown shape): convergent / divergent / shifting trajectory / authority-disputed
Expand Down
24 changes: 24 additions & 0 deletions docs/adr/0006-query-framing-open-implementation-problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 0006 — Query framing: open implementation problem, surface community practices

**Status:** accepted (2026-05-29) — broadens the persona in ADR 0005 §Decision.1; spec § 1.1 / § 2.10 updated to match

## Context

ADR 0005 fixed the demo-query persona as "stuck developer facing implementation **choice** — doesn't know which of several approaches to use." Module-D probe grilling (on the `q54069253` "useState not updating" canonical chain) showed this is too narrow:

- The answer-rich, recurring React questions in the corpus — and the persona-real ones — are **open problems** ("why doesn't this work / how do I do X"), not pre-named A-vs-B choices. The "Redux vs Context"-style explicit choice debates are scarce in the 2023–2026 window (they sit in the pre-backfill 2019–2021 era).
- Yet such open problems DO surface multiple community approaches in their answers — the "choice" is **emergent in the breakdown**, not stated in the query. Forcing a choice framing onto the query would exclude most of the usable corpus.

## Decision

A **query** is an open developer-facing implementation problem — *"what are the implementation approaches for this problem?"* — NOT a pre-named choice between alternatives. The research output is the **emergent community-practice breakdown** (authority-weighted) for that problem; any "choice" is read off the breakdown, not presupposed by the query.

- This **broadens** ADR 0005's persona (it subsumes the implementation-choice case) and keeps the rest of ADR 0005 intact: the shape / topic / temporal strata and the data-driven within-stratum selection still apply.
- Query selection still favours problems with **practice diversity** (multiple legitimate approaches); single-fix bug questions (one obvious answer) are filtered out — their breakdown is trivial.
- Downstream (ADR 0007): the consensus object is the **prescription** (recommended action). Pure-diagnosis answers (explaining *why* without proposing an action) are excluded — diagnosis convergence is correctness, not a preference-consensus signal.

## Consequences

- More of the 2023–2026 corpus becomes valid demo material (how-to / debugging problems with diverse fixes), reducing dependence on the 2019–2022 backfill for demonstrating *mechanics* (cross-year *trajectory* still needs backfill).
- CONTEXT.md `Query` term updated with this framing.
- The ADR 0005 selection-bias limitation persists: chosen problems are still researcher-selected, not universal.
Loading