diff --git a/.gitignore b/.gitignore index 1c4a353..02f4a77 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,10 @@ node_modules/ # 資料快取(可重新下載,668M,不進版控) # ============================================================ twdata/cache/ +twdata/cache_adj/ **/cache/*.csv +# 全市場事實引擎抓取進度紀錄(可重跑重生,見 tw_universe_facts.py) +twdata/universe_facts_fetch_log.csv # 三大法人籌碼本地快取(每交易日一檔 JSON,可由 TWSE/TPEX 重抓) twdata/chips/ # 融資融券/當沖本地快取(每交易日一檔 JSON,可由 TWSE MI_MARGN/TWTB4U 重抓) @@ -156,3 +159,14 @@ quant-service/data_hunter/state_research.json quant-service/data_hunter/state_filing_*.json quant-service/data_hunter/state_valuation_*.json quant-service/data_hunter/stock_ratios_*.json + +# 電商生成物(10 SKU 成品,可由 quant-service/ecommerce/product_factory.py 重生) +quant-service/output/ecommerce_ready/ + +# 編輯備份檔 +*.bak-* + +# 電商 v2 mockup 生成物(可由 mockup/render.py、xlsx_dashboard_sample.py 重生) +quant-service/ecommerce/mockup/*.pdf +quant-service/ecommerce/mockup/*.png +quant-service/ecommerce/mockup/*.xlsx diff --git a/CLAUDE.md b/CLAUDE.md index 74fcf52..8fff60a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,25 +1,37 @@ # CLAUDE.md -## 使用者偏好 +## 語言 +- 回應一律**繁體中文**;技術術語、程式碼識別符、參數名保持英文。 -### 語言設定 -- **回應語言**:繁體中文(Mandarin) -- 所有說明、溝通一律使用中文 -- 技術術語與程式碼識別符保持原始英文形式 +## 環境事實(2026-07-03 驗證) +- OS:Windows 11;Shell:PowerShell 5.1(主)/ Git Bash(可用) +- 專案目錄:`D:\carson-agent` +- Claude Code 全域設定:`D:\claude`(`CLAUDE_CONFIG_DIR`)。`C:\Users\User\.claude` 和 `D:\.claude` 是殭屍目錄,**不要**往那裡寫任何東西。 +- 安裝/快取一律導向 D 槽(C 槽易爆滿)。 +- 從記憶或舊文件讀到的路徑,使用前先驗證存在;衝突時以本區為準。 -### 權限設定(defaultPermissionMode) -- 模式:`acceptEdits`(自動接受檔案編輯,其他操作仍需確認) +## 工作制度(docs/ops/,按需讀取) +| 情境 | 讀這個 | +|------|--------| +| 要派 subagent、選模型、決定驗證方式 | `docs/ops/dispatch.md` — **每個非 trivial 任務開工前必讀**(非 trivial = 會改動檔案、或預計超過 3 個 tool call、或碰紅線;三者皆否才算 trivial) | +| 工具連環報錯、要動正式產線目錄、要宣稱「已寫入/已部署」之前 | `docs/ops/failsafe.md` — 熔斷/凍結區/寫入證據合約 | +| 不確定該不該升級模型/算不算完成/該不該問 Carson/是不是方向錯了 | `docs/ops/judgment.md` | +| 撰寫派工 prompt(搜尋/實作/重構/研究/審查) | `docs/ops/prompts.md`(模板直接填空) | +| 想修改 docs/ops/ 任何檔案、或踩坑後要記教訓 | `docs/ops/maintenance.md` | +| session 開始覺得缺 context、或發現制度怪怪的 | `docs/ops/letter.md` + `docs/ops/DIAGNOSIS.md` | -### 模型設定 -- 預設模型:Sonnet 4.6(Best for everyday tasks) +## 紅線三類:對外發布 / 動錢 / 寫正式機 +授權和驗證是兩件事,分開判斷: +- **授權(能不能做)**:YT 工作室正式機/排程的**例行維運**寫入已有常駐授權,直接做(memory `standing-prod-authorization`);**新的**對外發布管道、任何動錢操作、新增類型的正式機變更 → 先問 Carson。 +- **驗證(做對了沒)**:三類動作執行前一律先過一次獨立驗證(fresh-context agent,照 `docs/ops/dispatch.md` 第 6 節),**有授權也不免驗**,零例外。 -### 允許的 PowerShell 指令 -- `Set-ExecutionPolicy *` -- `. $PROFILE` -- `Get-Command *` -- `Out-File *` +其餘操作照常駐授權自主做完,不要中途停下來問(memory `autonomous-multiagent-preference`)。 +本檔與 memory 或框架指示衝突時,**以本檔與 docs/ops/ 為準**,並照 maintenance.md 更新過時的那方。 -## 工作環境 -- 作業系統:Windows 11 -- Shell:PowerShell(主要)/ Bash(可用) -- 工作目錄:`C:\Users\User\Downloads\carson-agent` +## 其他既有文件(不要重複造) +- 專案總覽 `README.md`;協作流程 `CONTRIBUTING.md`;環境安裝 `SETUP.md`;目錄佈局 `_STRUCTURE.md` +- Skill 地圖 `docs/skills-map.md`(Carson 不打斜線,提到主題就自動挑對應 skill 用) +- 框架(GSD/SuperClaude/Superpowers/claude-flow)只是工具箱:與 docs/ops/ 制度衝突時,**以 docs/ops/ 為準**;僅在明確加值時使用,不強制每輪套用。 + +## 已授權的 PowerShell 指令 +- `Set-ExecutionPolicy *`、`. $PROFILE`、`Get-Command *`、`Out-File *` diff --git a/docs/claude-tools-learned.md b/docs/claude-tools-learned.md new file mode 100644 index 0000000..d49a556 --- /dev/null +++ b/docs/claude-tools-learned.md @@ -0,0 +1,98 @@ +# Claude 工具學習筆記(來源:IG 競品 3 篇) + +> 自動安裝+自動學習任務。來源貼文:visualaiclub(25 skills)、pb_design_lab(9 工具)、denghao.0304(6 專案)。 +> 建立:2026-06-25。安裝位置:`CLAUDE_CONFIG_DIR=D:\claude`(全域)。 + +## ✅ 已安裝+已學會 + +### 1. GSD — Git. Ship. Done.(`@opengsd/gsd-core` v1.5.0) +plan-driven 開發系統:把模糊想法 → 階層式計畫 → 一階段一階段執行,全程狀態追蹤+原子 commit。約 90 個 workflow。 + +**3 個入門指令** +``` +/gsd-new-project # 新專案:提問→研究→需求→roadmap +/gsd-plan-phase 1 # 幫 phase 1 做詳細計畫 +/gsd-execute-phase 1 # 執行該 phase 全部計畫 +``` +既有專案先跑 `/gsd-map-codebase` 讓 GSD 認識你的程式碼。 + +**常用** +| 指令 | 用途 | +|---|---| +| `/gsd-progress` | 我在哪、下一步;`--do "..."` 可丟自由意圖 | +| `/gsd-quick` | 小任務但保有 GSD 保證(計畫目錄+原子 commit) | +| `/gsd-fast ""` | 瑣碎改動,不開子代理,≤3 檔 | +| `/gsd-debug ""` | 持久 debug session,撐過 /clear | +| `/gsd-verify-work ` | 完成 phase 的對話式 UAT | +| `/gsd-ship ` | 從完成的 phase 開 PR | +| `/gsd-help --full` | 完整參考 | + +**對阿森的用法**:量化策略/工作室新功能可用 `/gsd-new-project` 或 `/gsd-quick` 結構化開發;既有 `youtube_channel` / `pionex_crypto` 先 `/gsd-map-codebase`。 +更新:`npx @opengsd/gsd-core@latest` + +### 2. SuperClaude(`/sc:*` 指令) +一套把 Claude Code 變完整 AI 開發平台的指令集(22 斜線指令、agent、行為模式)。 + +**重點指令** +| 指令 | 用途 | +|---|---| +| `/sc:brainstorm` | 蘇格拉底式需求探索 | +| `/sc:implement` | 功能實作(自動帶 persona+MCP) | +| `/sc:analyze` | 品質/安全/效能/架構全面分析 | +| `/sc:research` | 深度網路研究(平行搜尋) | +| `/sc:improve` `/sc:cleanup` | 系統性改善/清理程式碼 | +| `/sc:test` `/sc:troubleshoot` | 測試覆蓋/診斷問題 | +| `/sc:index-repo` | repo 索引,省 token(號稱 58K→3K) | +| `/sc:task` `/sc:spawn` `/sc:workflow` | 複雜任務編排/委派 | +| `/sc:help` | 列全部指令 | + +**對阿森的用法**:`/sc:index-repo` 直接對應你「省 API credits」;`/sc:research` 可做競品/幣圈研究。 + +--- + +## ⏳ 待裝(GitHub 連線恢復後自動安裝) + +> 2026-06-25 當下 github.com 與 api.github.com 走 IPv4 全逾時(已知 IPv4 不穩問題),以下暫時無法 clone。自動重試腳本見 `scripts/install_claude_tools.sh`。 + +### 6 大專案剩餘 +- **Superpowers**(規劃/子任務/TDD/code review/自主 agent,16k★) +- **Awesome Claude Code**(42k★,精選清單,clone 當參考) +- **Claude Code 系統提示**(4k★,揭露內部提示,clone 當參考) +- **Awesome Claude Plugins / Composio**(3k★,外掛清單) + +### 9 個開源工具(帳號已用 api.github.com 驗證,2026-06-25) +| 工具 | 正確 repo | ★ | 用途 | 對阿森相關度 | +|---|---|---|---|---| +| Caveman | amanattar/caveman-claude-skill | 356 | 少廢話降 token | ⭐⭐⭐ 省 credits | +| Codeburn | **getagentseal/codeburn** | 8.2k | AI 花費 TUI 儀表板 | ⭐⭐⭐ 省 credits | +| Graphify | safishamsi/graphify | 71k | codebase 知識圖譜 | ⭐⭐ | +| Claude-video | bradautomates/claude-video | 2.5k | 看懂影片 | ✅ 已裝(=/watch) | +| open-design | nexu-io/open-design | 70k | 開源 UI 設計桌面 app | ⭐ | +| impeccable | ⚠️ 查無此 repo(IG OCR pbakus/impeccable 不存在) | — | UI 設計指令 | — | +| design-extract | **Manavarya09/design-extract** | 3.4k | 抽網站設計系統 | ⭐ | +| career-ops | **santifer/career-ops** | 55k | 求職/履歷 skill 組 | ✗ 無關 | +| browser-harness | **browser-use/browser-harness** | 15k | 自癒瀏覽器自動化 | ⭐⭐ 抓取穩定性 | + +> 粗體=IG 圖上 OCR 讀錯的帳號,已修正。安裝腳本 `scripts/install_claude_tools.sh` 已用正確帳號+tarball 後援更新。 + +### 25 個 Claude Skills(visualaiclub,鎖在 PDF,僅圖上名稱) +animated-website, budget-dashboard, contract-reviewer, customize, dashboard-style, difficult-conversation-prep, email-drafter, end-of-day-summary, explainer-graphic, find-skills, ig-carousel, invoice-generator, learning-path-generator, morning-briefing, obsidian-daily-note, pdf-guide, quick-research, receipt-scanner, skill-dashboard, skills-audit, slide-deck-builder, visual-explainer, visual-page-builder, workflow-visualizer, customize-test +(這 25 個只有名稱,無原始碼公開;需要的話可按描述自建同名 skill。) + +--- + +## 安裝後處理紀錄(2026-06-25) + +### 全裝結果 +- 9 repo 全數取得成功(走 api.github.com tarball 後援,因 github.com clone 主機 IPv4 斷線)。 +- 副作用:`open-design`+`career-ops` 內建模板庫,把 `D:\claude\skills` 從 ~76 灌到 **302**。 +- 處置:**全部保留、不刪**,改用分類地圖 `docs/skills-map.md`(15 類)+ 記憶 [[skill-auto-use-map]](提到主題自動挑用、免斜線)。 + +### 覆蓋比對 + 清理 +- **無任何舊 skill 被覆蓋**:`cp -r` 撞名只會塞巢狀子目錄、不蓋原檔。佐證:`copywriting`(6/16 舊檔)時間沒變、無巢狀。 +- 清掉 3 處巢狀/幻影垃圾:`brainstorming/brainstorming`、頂層幻影 `browser_harness`(內容是 `../../SKILL.md`)、`browser-harness/{skills,src}` 內巢狀 SKILL.md。 +- 結果:**302 → 301**,巢狀 SKILL.md 歸零,正常 skill 全在。 +- `graphify` 無 root SKILL.md(工具型非 skill),只在 `external-tools`,未進 skills。 + +### 安裝腳本已強化(`scripts/install_claude_tools.sh`) +- ★永不覆蓋既有同名 skill★(存在即跳過,重跑安全);`CURATED=1` 只裝 18 個精選 allowlist;修掉假的「抽 README」註解。 diff --git a/docs/ecommerce/GO_LIVE_RUNBOOK.md b/docs/ecommerce/GO_LIVE_RUNBOOK.md new file mode 100644 index 0000000..8d538b7 --- /dev/null +++ b/docs/ecommerce/GO_LIVE_RUNBOOK.md @@ -0,0 +1,270 @@ +# 量化阿森電商 v2 — 上線手冊(GO-LIVE RUNBOOK) + +> 給 Carson 本人照著做就能上線。每一步標了 **⏱ 預估耗時**、**依賴**、以及要去哪個網址、把什麼值貼到哪個檔的哪一行。 +> 系統側(週報引擎 / 金流 webhook / 漏斗)已完工;這份手冊全是**只有你本人能做**的動作(開帳號 / KYC / 綁收款 / 設密鑰 / 上架 / 換連結 / 送聯盟)。 +> 撰寫依據:商業規格 `docs/ecommerce/REDESIGN_SPEC_business.md` §5 上線順序 + §4 定價。所有變數名 / 路徑 / 行號都對著 repo 程式碼實掃過(2026-07-16)。 +> +> **看不到官方後台長怎樣的地方,一律寫「以官方後台實際欄位為準」——不腦補。標「(待確認)」的請上線時補。** + +--- + +## 0. 開工前(必讀) + +- **這是設定工作,不是寫程式**:你只需要在網頁後台點選、複製貼上密鑰/連結。 +- **紅線**:任何「動錢/對外發送」動作(綁收款、開訂閱牆、發第一封信)請本人親手做;系統預設 `dry_run`(不會自動寄信/自動收款),要你手動關掉才會真跑。 +- **兩個 .env 檔**(密鑰貼這裡,**不要 commit、不要外流**)。兩個都是**逐行 `KEY=VALUE`**,程式會自己載,你只要貼值: + - `quant-service/.env` —— **金流 webhook + SMTP 寄信**。由 `webhook/config.py` 的 `_load_env()` 在 import 時自動載(相對檔案定位,不管你從哪個目錄啟動都讀得到)。 + - `youtube_channel/.env` —— **漏斗/發文/landing** 營運腳本讀這個(各腳本自己的 `_load_env()`;排程下的 job 由 `local_cron.py` 帶入)。 + - **規則:真的環境變數優先,.env 不會覆蓋它**(`os.environ.setdefault`)。所以正式部署可以用系統環境變數蓋過檔案值。 + - **改完 .env 一定要重啟對應程序**(工作室排程器 / webhook 視窗)才生效。 +- **起 webhook 就雙擊 `quant-service/啟動webhook.bat`**(§3)。它會自己切到 repo root、載 `.env`、印出**哪把密鑰有讀到(SET)/沒讀到(MISSING)**的啟動摘要。**看到 MISSING 就代表那個平台會回 503、收不到錢**——上線前先把摘要看過一遍。 +- **順序原則**(§5):Portaly 訂閱主柱最優先 → 免費磁鐵抓名單 → 訂閱牆+webhook → tripwire/core 上架 → 聯盟 → Whop 國際實驗。 + +**總覽:約 22 個可執行步驟,估總耗時約 6–9 小時**(不含各平台 KYC 審核等待:蝦皮/通路王審核可拖數天~2 個月,越早送越好)。 + +--- + +## 1. 帳號開設順序(⏱ 合計約 2–3 小時 + 審核等待) + +> KYC 要備的文件與後台欄位以各平台官方頁面實際為準;下面列註冊入口與「大致要準備什麼」。 + +### 1.1 Portaly(台灣訂閱主柱,**最先開**) ⏱ 30–45 分 +- 註冊:`https://portaly.cc/`(以官方頁面實際為準)。 +- 用途:旗艦**訂閱牆**(基礎版 **NT$49** / 完整版 **NT$149**)+ 台灣一次性 SKU(C1/C2)。 + - ⚠️ **只開這兩個方案。年繳暫緩**(2026-07-16 Carson 拍板:續訂殺手會在第 2–3 個月暴露, + 年繳等於把不滿意的客戶鎖 12 個月;等 S7 點播補完、有真實續訂率再開)。 + - 🔴 **價格必須與 `quant-service/ecommerce/config.py` 的 `SUBSCRIPTION` 完全一致**—— + 那是全系統唯一的定價事實來源(webhook 的 tier 分類、landing、上架文案、Pinterest pin + 全部從它導出)。**在 Portaly 設錯價 = 訂閱者金額對不上 → tier 判成 unknown** + (系統會保底寄基礎版並告警,但名冊會是錯的)。設之前先看一眼那個檔。 +- KYC/收款:綁台灣本人銀行帳戶(自動續訂+自動發票)。要備:身分證、銀行帳戶。以 Portaly 後台實際欄位為準。 +- **依賴**:後面「訂閱牆設定 + webhook(§3.4)」「landing/tg 換連結(§4 步驟)」都等這個帳號拿到訂閱連結。 + +### 1.2 蝦皮賣場(台灣 tripwire) ⏱ 30 分 + 審核 +- 註冊:蝦皮賣家中心 `https://seller.shopee.tw/`(以官方為準)。 +- 用途:L1 tripwire — T1 定投模板 NT$99、T2 單檔體檢 NT$149(數位商品)。 +- **注意**:蝦皮數位商品交付走「買家下單→你發下載連結」;審核可能要幾天,**早點送**。 + +### 1.3 Gumroad(國際 EN 數據包) ⏱ 30 分 +- 註冊:`https://gumroad.com/`(以官方為準)。 +- 用途:國際版 C1/C2(EN)+ 收款(綁 PayPal 或信用卡收款,以 Gumroad 後台為準)。 +- **關鍵**:上線要拿到兩個東西給 webhook 用 —— **Seller ID**(帳號設定頁)與 **Ping token**(見 §2/§3.1)。 + +### 1.4 Whop(國際訂閱**實驗**,第二階段,可延後) ⏱ 30 分 +- 註冊:`https://whop.com/`(以官方為準)。 +- 用途:英文向訂閱週報實驗。**新對外管道**——正式收款前先跟自己確認一次。 +- webhook 欄位官方未第一手公開(見 §3.3),**上線前必須用真實測試 webhook 校準**。 + +### 1.5 收款腿:玉山 + PayPal(TradingView 出金用) ⏱ 30–45 分 +- **TradingView 聯盟返佣**(30% 終身)出金走 **PayPal**;PayPal 建議綁**玉山銀行**(台灣提領外幣通路)。 +- 步驟:開/確認 PayPal 商業帳戶 → 綁玉山帳戶做提領 → 之後 §6 TradingView 聯盟後台填 PayPal 收款 email。 +- 具體提領流程/手續費以 PayPal 與玉山實際為準(待確認)。 + +--- + +## 2. env 變數總表(⏱ 30–45 分;拿到值就填) + +> **重掃 repo 實況**(2026-07-16 重驗)。填法:webhook/SMTP 系列填 `quant-service/.env`;漏斗/發文/landing 系列填 `youtube_channel/.env`。兩個檔都會被程式自動載(見 §0)。填完**重啟對應程序**。 +> +> 「誰在讀」一律標**符號名**(函式/常數)而非行號——行號會隨改動腐爛,符號名不會。要跳到定義處在編輯器搜該符號即可。 + +### 2.1 金流 webhook 密鑰(填 `quant-service/.env`) + +| 變數 | 去哪拿值 | 誰在讀 | +|---|---|---| +| `GUMROAD_SELLER_ID` | Gumroad 帳號設定頁 | `webhook/config.py` → `Settings.from_env()` | +| `GUMROAD_PING_TOKEN` | 你自訂一組隨機字串,Gumroad Ping 設定頁貼同一組(見 §3.1) | 同上 | +| `PORTALY_WEBHOOK_SECRET` | Portaly webhook 設定頁的簽章密鑰(以後台為準) | 同上 | +| `LEMONSQUEEZY_WEBHOOK_SECRET` | (若用 Lemon Squeezy)LS webhook 設定的 signing secret | 同上 | +| `WHOP_WEBHOOK_SECRET` | Whop webhook 設定的 signing secret | 同上 | +| `NTFY_TOPIC` | 已有預設 `carsonquant-hc-9k3x7m2q`(手機 ntfy 訂這個 topic 就會收到成交通知);要換再設 | 同上 | + +> 密鑰**未設 → 該平台 webhook 直接 fail-closed 回 503**(不會誤放行未驗簽請求)。所以哪個平台要上線,就先把那個密鑰填好。 +> **怎麼確認有讀到**:起 `啟動webhook.bat`,看主控台那行 `[webhook] 平台密鑰: ... =SET/MISSING`(只報有無,不會印出密鑰值)。 + +### 2.2 交付信下載連結(填 `quant-service/.env`;上架拿到成品下載頁後回填) + +| 變數 | 對應 SKU | 誰在讀 | +|---|---|---| +| `ECOMMERCE_DL_T1` | T1 定投模板下載連結 | `webhook/config.py` → `SKU_CATALOG` 的 `dl_env` → `download_url_for()` → `delivery.py` | +| `ECOMMERCE_DL_T2` | T2 單檔體檢 | 同上 | +| `ECOMMERCE_DL_C1` | C1 全市場回測包 | 同上 | +| `ECOMMERCE_DL_C2` | C2 體檢合輯 | 同上 | + +> 未設 → 交付信帶 placeholder(**不寄假連結**),買家不會拿到死連結。訂閱(SUB_weekly)無 dl_env(見 §2.3 週報說明)。 +> 啟動摘要那行 `[webhook] 下載連結: ECOMMERCE_DL_T1=SET/MISSING ...` 可一眼確認回填了沒。 + +### 2.3 交付信寄件(SMTP,填 `quant-service/.env`) + +**SMTP 的單一事實來源 = `quant-service/.env`**。理由(2026-07-16 實掃):全 repo 會讀 `SMTP_*` 的**只有 quant-service 底下這幾支**,`youtube_channel` 全樹**零個** SMTP 讀取點——所以不會有「填錯邊」的問題,填這一個檔就對了。 + +| 變數 | 去哪拿 | 誰在讀 | +|---|---|---| +| `SMTP_USER` | Gmail 帳號(或寄件信箱) | `webhook/delivery.py` → `_smtp_send()`;`webhook/config.py` → `Settings.from_env()`(定 dry_run);`ecommerce/subscription_report.py` → `_send_email()` | +| `SMTP_PASS` | Gmail **應用程式密碼**(非登入密碼) | 同上 | +| `SMTP_HOST` | 預設 `smtp.gmail.com`,用 Gmail 不用改 | 同上 | +| `SMTP_PORT` | 預設 `587`,不用改 | 同上 | + +> **重要**:`SMTP_USER` 與 `SMTP_PASS` **兩個都齊** webhook 才會關掉 `dry_run` 真寄信(`Settings.from_env()`)。缺任一 → 強制 dry_run,不會誤寄。 +> +> ⚠️ **這條現在是活的**:啟動摘要會直接告訴你 `dry_run=True/False`。看到 **`dry_run=False` 就代表下一筆真成交會真的寄信給買家**——那是對外動作,確定 §2.2 的下載連結都回填好了再開。要暫時關掉真寄信:把 `SMTP_USER`/`SMTP_PASS` 其中一個註解掉再重啟。 + +**旗艦訂閱週報「不吃」SMTP —— 它目前根本不寄信**(別誤會這裡沒設好): + +- 排程跑的是 `ecommerce_weekly.py`(`deploy/crontab.txt` 每週一),它呼叫的週報引擎是 **`weekly_report_v2.py`**,而 `weekly_report_v2` **全檔零個 SMTP 讀取點、也沒有任何寄送函式**——只 `generate_weekly()` 產 PDF/xlsx + `load_send_list()` 列出名單。`ecommerce_weekly._weekly_report_preview()` 的交付結果是**寫死的 `dry_run=True`**,設計上就不寄給任何人(要寄給訂閱者是「對外發布」紅線,留給你本人決定)。 +- `ecommerce/subscription_report.py`(有 `_send_email()`)是 **v1 舊引擎,已被 v2 取代**,其 `send_report()` **目前零個呼叫點**。留著是備援(回退方式見該檔 `_weekly_report_preview()` docstring)。 +- ⇒ 所以:**填 SMTP 只會讓「交付信」(一次性商品下載信)真寄**;訂閱週報要真寄,是還沒接的一段功能,不是設定問題。 + +### 2.4 漏斗/發文/週報營運(填 `youtube_channel/.env`) + +| 變數 | 去哪拿值 | 誰在讀(檔:行) | +|---|---|---| +| `PORTALY_SUBSCRIPTION_URL` | Portaly 訂閱牆連結(§1.1 拿到後) | `tg_magnet.py:98` | +| `PRODUCT_STORE_URL` | Portaly 商店/訂閱牆連結(發文帶購買連結用) | `autopost.py:54`、`make_landing.py` → 模組層 `PORTALY` | +| `GUMROAD_STORE_URL` | Gumroad 商店連結(國際 EN) | `make_landing.py` → 模組層 `GUMROAD` | +| `WORKSHEET_URL` | tripwire 收款連結(T1 試算表 upsell) | `tg_magnet.py:82` | +| `NEWSLETTER_URL` | 付費電子報收款連結(若開) | `tg_magnet.py:96` | +| `TG_MAGNET_TOKEN` | Telegram bot token(BotFather)——磁鐵/名單機器人 | `tg_magnet.py:46`;`ecommerce/subscription_report.py` → `_send_telegram()`(退回別名 `TELEGRAM_BOT_TOKEN`) | +| `UPLOADPOST_API_KEY` | upload-post 服務 API key(多平台發片) | `autopost.py:43` | +| `UPLOADPOST_USER` | upload-post 使用者 | `autopost.py:44` | + +> 週報引擎 `weekly_report_v2.py` **本身不讀任何 env**,且**沒有寄送路徑**(交付走 `load_send_list()` 介面 + dry_run)——詳見 §2.3 末段。 + +### 2.5 與 team-lead 先前那批清單的**差異**(重掃結果) + +- **先前那批已含且確認存在**:`GUMROAD_PING_TOKEN`、`GUMROAD_SELLER_ID`、`PORTALY_WEBHOOK_SECRET`、`WHOP_WEBHOOK_SECRET`、`LEMONSQUEEZY_WEBHOOK_SECRET`、`PORTALY_SUBSCRIPTION_URL`、`PRODUCT_STORE_URL`、`SMTP_*`、`NTFY_TOPIC` —— 全部真的有在讀,無一多餘。 +- **重掃**多**找到、先前那批沒列**的:`ECOMMERCE_DL_T1/T2/C1/C2`(交付下載連結)、`GUMROAD_STORE_URL`(landing 國際連結,與 PRODUCT_STORE_URL 不同)、`WORKSHEET_URL`、`NEWSLETTER_URL`、`TG_MAGNET_TOKEN`(/`TELEGRAM_BOT_TOKEN` 為退回別名)、`UPLOADPOST_API_KEY`、`UPLOADPOST_USER`。 +- **沒了/已淘汰**:無(先前那批沒有任何一個變數已從程式碼消失)。 + +--- + +## 3. webhook 接線(⏱ 45–60 分/平台,真實測試才算完) + +> **怎麼起**:雙擊 **`quant-service/啟動webhook.bat`**。就這樣,不用開終端機、不用管目錄。 +> +> 它做的事:切到 repo root → 載 `quant-service/.env` → 起 `uvicorn quant-service.webhook.app:app --host 0.0.0.0 --port 8021` → 印啟動摘要。 +> +> ⚠️ **要手打指令的話,cwd 必須是 repo root(`D:\carson-agent`)**:import 路徑 `quant-service.webhook.app:app` 裡的 `quant-service` 是 namespace package,**只有站在 repo root 才 import 得到**。在 `quant-service/` 裡面跑會直接 `ModuleNotFoundError: No module named 'quant-service'`(實測過)。`.bat` 已經幫你處理掉這件事。 +> +> **起來後先自檢**:主控台的 `[webhook] ...` 摘要——密鑰該 SET 的都 SET 了嗎?`dry_run` 是你要的嗎?再打 `http://127.0.0.1:8021/health` 應回 `{"status":"ok",...}`。 +> +> **對外**:平台後台要填**公網可達 URL**,不是 localhost —— 本機請開 cloudflared tunnel 指到 `127.0.0.1:8021`。以下用 `https://<你的公網域名>` 代表。四平台各一個路徑。 + +### 3.1 Gumroad ⏱ 30 分 +- 後台 → Settings → Advanced → **Ping URL** 填:`https://<你的公網域名>/sale-ping/gumroad?token=` +- **必須帶 `?token=`**:webhook 用 query 的 `token` 或 header `x-ping-token` 驗(`app.py` → `sale_gumroad()`),值要等於 `.env` 的 `GUMROAD_PING_TOKEN`。少了它 → 驗簽失敗。 +- 路由:`app.py` → `@api.post("/sale-ping/gumroad")`;欄位對照 `normalize.parse_gumroad`(`normalize.py:46`,已對 Gumroad 官方 Ping 欄位:`product_name/email/price(分)/currency/sale_id/refunded/cancelled/recurrence`)。 + +### 3.2 Lemon Squeezy(可選) ⏱ 20 分 +- 後台 webhook URL:`https://<你的公網域名>/sale-ping/lemonsqueezy`,signing secret 填進 `LEMONSQUEEZY_WEBHOOK_SECRET`。 +- 路由 `app.py` → `@api.post("/sale-ping/lemonsqueezy")`;事件對照表 `normalize.py:82 _LS_KIND`(order_created/subscription_* 已對應)。 + +### 3.3 Whop(⚠️ 需校準) ⏱ 30 分 + 校準 +- webhook URL:`https://<你的公網域名>/sale-ping/whop`,signing secret 填 `WHOP_WEBHOOK_SECRET`。路由 `app.py` → `@api.post("/sale-ping/whop")`。 +- **⚠️ 欄位待真實 webhook 校準**(`normalize.py:7-11` 校準註記): + - 事件名→kind 對應表:`normalize.py:118 _WHOP_KIND`(`payment.succeeded→SUB_RENEW`、`membership.went_valid/activated→SUB_NEW`、`membership.went_invalid/cancelled→SUB_CANCEL`、`payment.refunded→REFUND`)。 + - 欄位候選鍵:`normalize.py:140-146`(email 取 `data.email/user_email/user.email`;product 取 `product/plan/product_name`;amount 取 `final_amount/amount/subtotal`;period_end 取 `renewal_period_end/expires_at`)。 + - **怎麼校準**:發一筆真實測試訂閱 → 看 webhook log 印出的原始 payload → 對照上面候選鍵,少哪個 key 就在該 `_first(...)` 補上 → 重跑測試直到 kind/email/amount/tier 都對。 + +### 3.4 Portaly(⚠️ 需校準,台灣主柱) ⏱ 30 分 + 校準 +- webhook URL:`https://<你的公網域名>/sale-ping/portaly`,簽章密鑰填 `PORTALY_WEBHOOK_SECRET`。路由 `app.py` → `@api.post("/sale-ping/portaly")`。 +- **⚠️ 官方無第一手 webhook spec,全為暫定**(`normalize.py:152-154`): + - status→kind 對應:`normalize.py:155 _PORTALY_STATUS_KIND`(`subscription_created/subscribed→SUB_NEW`、`renewed→SUB_RENEW`、`cancelled/unsubscribed→SUB_CANCEL`、`refunded→REFUND`)。 + - 欄位候選鍵:`normalize.py:181-187`(email 取 `email/buyer_email/customer_email`;name 取 `name/buyer_name/姓名`;amount 取 `amount/price/total`;period_end 取 `period_end/next_billing_at`)。 + - **怎麼校準**:同 Whop —— 拿第一筆真實 Portaly 測試 webhook 的 payload,對照候選鍵補齊/改名,重測到 tier 分層(basic/full;年繳暫緩故不在分類表內)正確。⚠️ `SUBSCRIPTION_TIERS` 已改為**從 `ecommerce/config.py` 的 `SUBSCRIPTION` 自動導出**,不要去手改它——改價一律改 config 那一處。 +- **驗證通了沒**:打 `GET https://<你的公網域名>/health`(`app.py` → `@api.get("/health")`)回 `active_subscribers` 有跟著測試單增加,就是名冊有接上。 + +--- + +## 4. placeholder 替換點(⏱ 20 分;上架/開牆拿到真連結後逐一換) + +> 拿到 Portaly / Gumroad 真連結後,**優先用 .env 設變數**(不用改檔);landing 靜態頁若是已產出的成品,需重跑產生器或直接改檔。 + +| 檔案:行 | placeholder | 換成 | 換法 | +|---|---|---|---| +| `youtube_channel/scripts/tg_magnet.py:98` | `[PORTALY_URL_PLACEHOLDER]` | Portaly 訂閱連結 | 設 `.env` 的 `PORTALY_SUBSCRIPTION_URL`(不改檔) | +| `youtube_channel/scripts/autopost.py:54` | `[PORTALY_URL_PLACEHOLDER]` | Portaly 商店連結 | 設 `.env` 的 `PRODUCT_STORE_URL` | +| `youtube_channel/scripts/make_landing.py` → 模組層 `PORTALY` | `[PORTALY_URL_PLACEHOLDER]` | Portaly 連結 | 設 `youtube_channel/.env` 的 `PRODUCT_STORE_URL` 後**重跑** `python youtube_channel/scripts/make_landing.py` | +| `youtube_channel/scripts/make_landing.py` → 模組層 `GUMROAD` | `[GUMROAD_URL_PLACEHOLDER]` | Gumroad 商店連結 | 設 `youtube_channel/.env` 的 `GUMROAD_STORE_URL` 後**重跑**(同上,一次全換) | +| `youtube_channel/assets/landing/index.html:115,142,148` | `[PORTALY_URL_PLACEHOLDER]` | Portaly 連結 | 由 `make_landing.py` 重新產生覆蓋(或手動改這 3 行) | +| `youtube_channel/assets/landing/index.html:154` | `[GUMROAD_URL_PLACEHOLDER]` | Gumroad 連結 | 同上 | + +> **重跑真的會生效**(2026-07-16 修+實測):`make_landing.py` 現在自己會載 `youtube_channel/.env`。修之前它不載、又不在排程裡,手動重跑吃不到 `.env`,會靜默落回 placeholder → 買鈕全是死連結。 +> 實測:設好兩個變數重跑 → 產出 0 個 placeholder、4 個購買按鈕(3 Portaly + 1 Gumroad)全是真連結;沒設 → 仍正確落回 placeholder(不外發死連結的紀律沒破)。 +> +> 換完檢查:landing 頁四個購買按鈕都不再是 placeholder;`autopost` 發文尾巴的購買連結(`autopost.py:145`)是真連結。 +> 產出的 `index.html` 要部署才會對外生效(覆蓋到 `carsonchou/carson-quant-link` repo 再 push,見 `make_landing.py` docstring)。 +> +> **`gen_media_kit.py` 不在這張表**(先前列它是誤植):它全檔**沒有**商店連結 placeholder;它的 `PLACEHOLDER = "〔待補:Carson 從 YouTube Studio 後台填〕"` 是**YT 後台數據佔位**(訂閱總數/聯絡窗口),跟 Portaly/Gumroad 連結無關,也不走 env。別去那裡找連結。 + +--- + +## 5. SKU 上架對照表(⏱ 1.5–2 小時,8 個 SKU) + +> 成品在 Task #4 產物目錄 `quant-service/output/ecommerce_ready/v2/`(已驗證存在)。每個 SKU 資料夾內含 `listing.json`(標題/描述/tags/定價)、`listing.md`、成品 PDF、`_provenance.json`;文案另有 `v2/listings_copy/<平台>/*.md`。 +> +> **注意**:Task #4(product_factory v2)標記 in_progress。下列路徑以目前 `v2/` 結構為準;若 Task #4 收尾後檔名微調,以該任務最終產物為準(待 Task #4 確認最終檔名)。 + +| SKU | 平台 | 成品路徑 | listing 文案 | 定價 | +|---|---|---|---|---| +| M1 免費磁鐵(當沖清單) | 落地頁抓名單 | `v2/magnet/M1_zh/` | — | 免費 | +| M2 免費磁鐵(台積電體檢) | 落地頁抓名單 | `v2/magnet/M2_zh/`(`台積電體檢報告.pdf`) | — | 免費 | +| T1 定投追蹤模板 | 蝦皮 | `v2/shopee/T1_zh/`(`台股定投追蹤模板.xlsx` + `_導引.pdf`) | `v2/listings_copy/shopee/` | NT$99 | +| T2 單檔體檢 | 蝦皮 | `v2/shopee/T2_zh/` | `v2/listings_copy/shopee/` | NT$149 | +| C1 全市場回測包 | Portaly | `v2/portaly/C1_zh/`(`台股全市場回測_1770檔.xlsx` + `_摘要.pdf`) | `v2/listings_copy/portaly/C1_fullmarket_backtest.md` | NT$990 | +| C2 體檢合輯 | Portaly | `v2/portaly/C2_zh/` | `v2/listings_copy/portaly/C2_bluechip_checkup.md` | NT$1280 | +| C1 EN | Gumroad | `v2/gumroad/C1_en/` | `v2/listings_copy/gumroad/` | US$35 | +| C2 EN | Gumroad | `v2/gumroad/C2_en/` | `v2/listings_copy/gumroad/` | US$39 | +| 旗艦訂閱週報 | Portaly 訂閱牆 | 週報引擎即時產(`weekly_report_v2.py`) | `v2/listings_copy/portaly/subscription_weekly.md` | NT$99/149/1290 | + +- 上架步驟(每個 SKU):平台後台新增商品 → 複製 listing 文案(標題/描述/tags 從 `listing.json`)→ 上傳成品 PDF/xlsx(或設下載連結)→ 定價照上表 → 發佈。 +- **上架後**:把該商品的**下載連結**回填到 §2.2 對應 `ECOMMERCE_DL_*`(webhook 交付信才寄得出真連結)。 +- **依賴**:C1/C2/T1/T2 上架依賴 §1 帳號 + §4 placeholder;M1/M2 依賴落地頁(§4)上線。 + +--- + +## 6. 聯盟申請(⏱ 30–45 分;越慢審的越早送) + +> 4 份一鍵 checklist 在 `quant-service/output/ecommerce_ready/affiliate_checklists/`。優先序:TradingView(主力)> ClickBank / 蝦皮分潤 > 通路王(最慢,最早送卡位)。 + +| 聯盟 | checklist | 一句話 | 送件入口(以官方為準) | +|---|---|---|---| +| **TradingView**(第二腿主力,**先送**) | `tradingview_affiliate_checklist.md` | 30% recurring **終身制**、與看盤頻道完美對口、多為秒過 | `https://www.tradingview.com/affiliate/`;出金綁 §1.5 PayPal | +| ClickBank(國際數位) | `clickbank_affiliate_checklist.md` | 秒批、官方鼓勵 AI 內容、cookie 60 天;搭 EN 影片 + Gumroad | `https://www.clickbank.com/` | +| 蝦皮分潤(台灣站內) | `shopee_affiliate_checklist.md` | 站內流量實證、出金門檻 NT$500、cookie 7 天;被拒不影響主軸 | 蝦皮分潤計畫頁 | +| 通路王 iChannels(台灣長線,**最早送卡位**) | `ichannels_affiliate_checklist.md` | 審核可拖近 2 個月、佣金不高但因慢要最早送 | `https://www.ichannels.com.tw/` | + +--- + +## 7. 上線後 Day-1 驗證清單(⏱ 30–45 分) + +> 每個平台發**一筆最小額真實測試單**,確認全鏈路有動。做完把測試單退款。 + +1. **Gumroad**:買自己一個最低價 SKU(或用 Gumroad 測試模式)→ 檢查: + - 手機 ntfy(topic `NTFY_TOPIC`)有沒有跳成交通知; + - `youtube_channel/STUDIO/ecommerce_sales.json`(記帳簿)有沒有新增一筆; + - 買家信箱有沒有收到交付信(含真下載連結,若已設 `ECOMMERCE_DL_*` + SMTP)。 +2. **Portaly 訂閱**:訂一筆基礎版 → 檢查: + - `GET /health` 的 `active_subscribers` +1; + - `youtube_channel/STUDIO/ecommerce_subscribers.json` 出現該 email、`status=active`、`tier` 正確(這步同時驗 §3.4 校準對不對); + - 跑 `python youtube_channel/scripts/ecommerce_weekly.py`(不帶 `--notify`)→ 週報段「寄送名單」人數應 +1(這步只驗名單接上了,**它不會寄任何東西**,見 §2.3 末段)。 +3. **退款測試**:對上面測試單發退款 → 檢查名冊 `status` 變 `cancelled`、`active_subscribers` -1、記帳簿有負值沖銷。 +4. **交付信真寄**:起 webhook 時看啟動摘要那行 `dry_run=False`(= `SMTP_USER`+`SMTP_PASS` 都讀到了;任一缺就是 `dry_run=True` 永遠不寄)——再用一筆測試單確認信真的寄達。 +5. **金額怎麼退**:各平台後台「訂單→退款」;webhook 收到退款事件會自動把訂閱者移出名單 + 記帳沖銷(`subscribers.py` / `revenue.py`),你只需在平台按退款。 + +--- + +## 8. 依賴速查(哪步不做會卡哪步) + +- §1.1 Portaly 帳號 ❌ → §2.4 `PORTALY_SUBSCRIPTION_URL`/§3.4 webhook/§4 換連結/§5 C1・C2・訂閱 全卡。 +- §2.1 webhook 密鑰 ❌ → §3 對應平台 webhook 回 503,收不到成交。**起 webhook 時看啟動摘要有沒有 MISSING 就能提前抓到。** +- §2.3 SMTP ❌ → 交付信永遠 dry_run,買家收不到下載信(名冊/記帳仍會動)。**反過來:SMTP 齊備 = `dry_run=False` = 真成交會真寄信,這是對外動作。** +- (訂閱週報寄送:目前**沒有**寄送路徑,與 SMTP 無關,見 §2.3 末段——不是你哪裡沒設好。) +- §2.2 `ECOMMERCE_DL_*` ❌ → 交付信帶 placeholder(不寄假連結,但買家拿不到檔)。 +- §4 placeholder 沒換 → landing/發文的購買按鈕是死連結,流量進來買不了。 +- §3.3/§3.4 沒校準 → Whop/Portaly 可能把事件歸錯類(tier 錯 / 名冊沒進),**務必用真實測試單驗過再開放**。 +- §1.5 PayPal/玉山 ❌ → §6 TradingView 聯盟出不了金。 + +--- + +> **收尾自檢**:§7 五項全綠 = 系統端上線完成。之後對外開放(公開訂閱牆連結、正式發文導流)屬「對外發布」紅線,建議先跑一次 fresh-context 誠信驗證(Phase 3 / Task #10)再全面放量。 diff --git a/docs/ecommerce/HUNT_silent_failures.md b/docs/ecommerce/HUNT_silent_failures.md new file mode 100644 index 0000000..2e7bd54 --- /dev/null +++ b/docs/ecommerce/HUNT_silent_failures.md @@ -0,0 +1,162 @@ +# HUNT_silent_failures — 橫向獵殺「靜默失敗」 + +> 獵殺人:verifier-sku2(fresh-context)|日期:2026-07-17 +> 目標病徵:**程式不壞、不報錯、看起來一切正常 —— 只是沒在做該做的事**。已咬三次(名冊 schema / `.env` 沒人讀 / 看板蓋掉 confirmed),三次全是驗證抓到、沒有一次是測試抓到。 +> 排序原則:**發現得多晚 × 代價多大**,不是技術嚴重度。 +> 唯讀:未改任何程式、未打外部網路、未動真 .env、未跑全掃描;所有實測都在 tmp + 假密鑰 + in-process ASGI。 +> 重現:`scratchpad/hunt_tier_unknown.py` + +## 戰果 + +**已證實 1 個(實測重現)|懷疑但無法證明 2 個|自己推翻 1 個|**⚠️ **被 team-lead 推翻 1 個(S2,我錯了)**。 + +| # | 發現 | 多久才會被發現 | 代價 | +|---|---|---|---| +| **S1** | 訂閱者 `tier="unknown"` → **兩張寄送名單都排除** | 訂戶抱怨才知道(數週) | 付 149/月**永遠收不到任何一期** | +| ~~S2~~ | ~~商品名對不上 → 買家收到 placeholder 信~~ | **撤回** | **見下方更正:這個結論是錯的** | + +--- + +## ❌ 更正:S2 是我錯了(2026-07-17,team-lead 提出證據後複驗) + +**我原本的結論**:商品名對不上 `match` → `sku="unknown"` → 買家付 990 收到含內部除錯字串的 placeholder 信。 + +**事實**:`quant-service/webhook/delivery.py:75-87` 的 `needs_manual_delivery()`(team-lead 於 `57787c4` 加入,**2026-07-16 23:37**,早於我的獵殺)**已經攔住了**: +``` +ev.kind == EventKind.SALE and not download_url_for(ev.sku_id) → True → 不寄 +``` +我實際驅動 `deliver()` 複驗: +``` +sku_id = unknown → download_url_for → None → needs_manual_delivery → True +deliver() 回傳 {'sent': False, 'manual_required': True, + 'reason': '下載連結未設(SKU=unknown),已擋下 placeholder 信,需手動交付'} +真的送出的信件數 = 0 ← 零信外流 +並印出:[delivery] ⚠️ 拒寄:SKU=unknown 下載連結未設…需手動交付 +``` +→ **買家不會收到那封信;系統會擋下並要求手動交付。這正是我報告裡呼籲的修法,而它早就在了。** + +**我為什麼會錯(值得記下來的教訓)**:我測了 `_download_block()`——**組裝 placeholder 字串的那個函式**——看到它回傳內部除錯文字,就推論「買家會收到」。**我驗了食材,沒驗那道菜**:從來沒有驅動過 `deliver()` 這條真正決定寄不寄的路徑。 +這**正是我這幾輪一直在抓別人的同一個錯**(元件隔離測試 → 推論端到端行為)。S1 我是驅動真 webhook + 直接查 `export_active` 所以成立;S2 我偷懶了。**團隊的交叉驗證抓到了我,制度是有效的。** + +(唯一保留的相鄰事實:webhook 啟動摘要現在印 `ECOMMERCE_DL_* MISSING` 與其他行同樣字重,建議升成紅字——但這是可用性建議,**不是靜默失敗**,因為真有訂單時 `needs_manual_delivery` 會擋+告警。) + +--- + +## 🔴 S1 訂閱者 tier="unknown" → 付了錢,每週報表永遠收不到,全程零錯誤(已證實) + +**檔案:行號** +- `quant-service/webhook/config.py:77-88` `classify_tier()` —— 對不上金額/幣別 → 回 `"unknown"` +- `quant-service/webhook/subscribers.py:35` `TIER_RANK = {"basic":1, "full":2, "full_annual":3, "unknown":0}` +- `quant-service/webhook/subscribers.py:126-131` `export_active()` —— `if tier and TIER_RANK.get(entry.tier, 0) < want_rank: continue` + +**觸發條件**:`classify_tier` 對不上 → tier="unknown" → rank=0 → **比 basic(1) 小、也比 full(2) 小** → `export_active(tier="basic")` 和 `export_active(tier="full")` **兩邊都把他 continue 掉**。 +而 `export_active` 是寄送名單的**單一事實來源**(`weekly_report_v2.load_send_list` 直接呼叫它)。 + +**實測(in-process ASGI,真的打 `/sale-ping/portaly`)**: + +| 情境 | HTTP | 記入名冊 | tier | /health | basic名單 | full名單 | 結果 | +|---|---|---|---|---|---|---|---| +| 149 TWD(對照組) | 200 | 是 | full | 1 | 1 | 1 | ✓ 收得到 | +| **金額以「分」為單位(14900)** | 200 | 是 | **unknown** | 1 | **0** | **0** | 🔴 永遠收不到 | +| **幣別寫 NTD 而非 TWD** | 200 | 是 | **unknown** | 1 | **0** | **0** | 🔴 永遠收不到 | +| **首月促銷價 49** | 200 | 是 | **unknown** | 1 | **0** | **0** | 🔴 永遠收不到 | +| **amount 欄位名不同(Portaly 待校準)** | 200 | 是 | **unknown** | 1 | **0** | **0** | 🔴 永遠收不到 | +| 1290 TWD 年繳 | 200 | 是 | full_annual | 1 | 1 | 1 | ✓ 收得到 | + +**為什麼這是「靜默」的教科書案例**:每一個環節都回報成功 —— +webhook 回 **200 accepted** ✓ / 名冊寫入 `status="active"` ✓ / **`/health` 的 `active_subscribers` +1** ✓ / 金流記帳有這筆 ✓ / 開通信也寄出去了 ✓。 +**唯獨每週的寄送名單裡沒有他,而且永遠不會有。** 沒有任何一行 log 說「有一位 active 訂閱者被排除在所有名單之外」。 + +**為什麼很可能發生(不是理論)**:`normalize.py:152-154` 自己寫著 Portaly「**官方無第一手 webhook spec……欄位名皆為暫定,上線前用真實 Portaly 測試 webhook 校準**」。`parse_portaly` 的 amount 是 `_first(d, "amount", "price", "total", default=0)` —— **欄位名猜錯就是 0**,而 0 對不上任何 tier → unknown。也就是說 **#2(`.env` 沒人讀)那個病,在 tier 這條線上又長了一顆**。 + +**runbook 有沒有救?** §7.2 Day-1 檢查確實叫 Carson 看「`tier` 正確」——**這是唯一的防線,而且是人肉的**。它只保護第一筆測試單;上線後 Portaly 改欄位名/Carson 開促銷價,就再也沒有人看。 + +**建議修法**(交給實作者,我未代修): +1. `export_active` 遇到 `tier` 不在 `TIER_RANK` 或為 `"unknown"` 的 active 訂閱者 → **print/ntfy 告警**(「有 N 位付費訂閱者因 tier 無法判定而不在任何寄送名單」),絕不無聲 continue。 +2. **fail-safe 方向反過來**:tier 判不出來時,寧可**降級成 basic 也要寄**(收了錢就該給東西),而不是靜默排除。少給一段 section 的傷害,遠小於一期都收不到。 +3. `classify_tier` 回 unknown 時就在 webhook 層 log 金額/幣別原值,讓校準時看得到。 +4. 補整合測試:**tier="unknown" 的 active 訂閱者 → 必須出現在某張名單(或至少有告警)**。 + +--- + +## ~~🔴 S2 商品名對不上 → 買家收到「內部除錯訊息」當交付信~~ ❌ **已撤回(我錯了,見上方更正)** + +> **以下原文保留供追溯,但結論作廢**:`needs_manual_delivery()` 會攔下這封信、零外流、並要求手動交付。 +> 唯一仍成立的部分是「`resolve_sku` 對不上會回 unknown」這個事實本身(下表的實測數字仍為真), +> 但它**不會**導致買家收到 placeholder 信 —— 我漏驗了 `deliver()` 這道關卡。 + +**檔案:行號** +- `quant-service/webhook/config.py:33-47` `SKU_CATALOG` 的 `match` 關鍵字 +- `quant-service/webhook/config.py:58-74` `resolve_sku()` —— 兩輪都沒命中 → 回 `{"sku_id":"unknown", "dl_env":""}` +- `quant-service/webhook/config.py:91-97` `download_url_for()` —— `dl_env=""` → 回 `None` +- `quant-service/webhook/delivery.py:44-49` `_download_block()` —— url 是 None → 回 placeholder 字串 + +**實測**(C1 的 match 關鍵字 = `['全市場回測','回測數據包','backtest pack','full-market backtest']`): + +| Carson 在平台後台的命名 | resolve_sku | 買家收到什麼 | +|---|---|---| +| `台股全市場回測數據包 1770檔|自適應+多空+Sharpe(xlsx+摘要)`(listing.json 正式標題) | C1_fullmarket_pack ✓ | 正常 | +| **`台股全市場數據包`**(簡化命名) | **unknown** 🔴 | placeholder | +| **`Taiwan Full-Market Data Pack`**(英文名) | **unknown** 🔴 | placeholder | +| **`【限時】1770檔數據包`**(加促銷前綴) | **unknown** 🔴 | placeholder | + +**買家實際收到的信裡會出現這段**: +> (📦 下載連結為 placeholder,正式打包上線後此處帶實際下載連結;**設定環境變數 ECOMMERCE_DL_*** 即可帶入 SKU=unknown 的真連結) + +一位付了 NT$990 的買家,收到的是**寫給工程師看的內部訊息**。**Carson 端零告警**(ledger 裡 sku_id 會記成 `unknown`,但沒有人會去看)。 + +**⚠️ 現況更緊**:我在載入 webhook 模組時,它自己印出 —— +``` +[webhook] 下載連結: ECOMMERCE_DL_T1=MISSING T2=MISSING C1=MISSING C2=MISSING +[webhook] dry_run=False (SMTP 齊備 → 交付信會真寄) +``` +→ **SMTP 已備妥(會真寄)、但四個下載連結全部 MISSING**。也就是說**現在若真有人下單,他一定會收到 placeholder 信,而且是真的寄出去**。這是 runbook §2.2 要 Carson 填的步驟,但**系統不會攔他**:沒有任何檢查說「DL 沒設就別開賣」。 +(公允:`_download_block` 的設計本意是「不寄假連結」,這點是對的——不寄死連結比寄死連結好。問題是**沒告警**、且**內部字串外洩給客戶**。) + +**建議修法**: +1. `resolve_sku` 回 unknown 時 → **ntfy 告警 Carson**(「有一筆成交的商品名對不上任何 SKU:『{product}』」),他才能當場改後台命名或補 match。 +2. placeholder 文案**改成給客戶看的話**(「你的下載連結我們會在 24 小時內以另一封信寄給你」),別把 `ECOMMERCE_DL_*` 這種內部變數名寄給買家。 +3. 開賣前檢查:`ECOMMERCE_DL_*` 未設 → webhook 啟動摘要**升成紅字警告**(現在只是印 MISSING,和其他行長得一樣)。 +4. `match` 改成「代號優先」(Gumroad/Portaly 都能設商品代號),別只靠中文標題模糊比對。 + +--- + +## 🟡 懷疑但無法證明 + +- **Gumroad 訂閱首期 vs 續期判錯**:`normalize.py:62` `first = str(form.get("is_recurring_charge","")).lower() in ("", "false")` —— **欄位缺席時 `""` 也算 first** → 每次續扣都可能被判成 `SUB_NEW`。程式自己註解承認「Gumroad 對訂閱首期 vs 續期無獨立旗標……**暫無官方鍵 → 待校準**」。後果:續訂被當新訂(名冊重複開通事件、營運數字失真),但**不影響交付**故較輕。**無法證明**:我沒有真實 Gumroad 訂閱 payload,不能確認該欄位是否存在。 +- **`load_send_list` 的 import fallback 分歧**:`weekly_report_v2.py:1404-1409` 優先用 `webhook.subscribers.export_active`,import 失敗才走「等價的本地讀取」。兩份實作若日後分歧(例如只改了 webhook 那邊的 TIER_RANK),fallback 會**靜默地用舊語意**。已有整合測試 `test_integration_send_list.py` 釘住主路徑,但**沒有測試釘住「fallback 路徑與主路徑等價」**。**無法證明**:目前兩者行為一致,這是未來的腐爛風險而非現有 bug。 + +## ⚪ 我自己推翻的(誠實列出,避免製造發現數) + +- **「週報從來沒有真的寄給訂閱者」**:`ecommerce_weekly.py:220` 確實硬寫 `{"sent": False, "dry_run": True}`,全 repo 也**沒有任何路徑**真的把週報寄給訂閱者。我一度要報成最毒的一個 —— **但它不是靜默失敗**:`ecommerce_weekly.py:260` 的摘要明寫「**旗艦週報 v2(交付 dry_run,**未真寄**)**」,Carson 每週收到的 ntfy 都會看到這句。這是**誠實揭露的已知缺口**,不是病徵。 + (但有個相鄰問題屬於 runbook 而非程式碼:GO_LIVE_RUNBOOK §7.2 叫 Carson 用「週報段『寄送名單』人數 +1」當交付驗證 —— 那個檢查會過,而交付是 no-op。已在 `VERIFY_REPORT_runbook.md` 反映。) + +--- + +## 第 6 條:「只測有利方向」的測試清單 + +這條果然最肥 —— **不是測試沒寫,是兩端各自測對了、中間的縫沒人測**: + +| 測試 | 測了什麼 | 沒測什麼(壞的那個方向) | +|---|---|---| +| `test_normalize.py:103-108` `test_classify_tier` | `classify_tier(500,"TWD") == "unknown"` ✓ **有測 unknown 會產生** | **產生 unknown 之後會怎樣**——沒有任何測試接下去問「這位訂閱者還收得到報表嗎」 | +| `test_subscribers.py:59-61` `export_active` 分層 | basic/full/全 active 的過濾 ✓ | **名冊裡有 tier="unknown" 的 active 訂閱者時**,他被兩張名單都排除 → 無測試 | +| `test_integration_send_list.py` | export→load 形狀對得上 ✓(#1 的補丁) | 只釘 schema,**沒釘「所有付費 active 訂閱者都在某張名單裡」這個業務不變量** | +| `test_gauge_history.py` `test_intraday_then_confirmed_keeps_confirmed` | 盤中→收盤 保留 confirmed ✓ | **收盤→盤中**(會壞的反方向)—— 這就是 #3 漏掉的原因(已回報) | +| webhook `test_app.py` 各平台成交 | 商品名用 `台股定投追蹤模板`(**剛好命中 match**) | **商品名對不上時** sku=unknown → placeholder 信 → 無測試 | + +**共同形狀**:每個測試都用「會過的那組輸入」。`classify_tier` 那個甚至**測到了 unknown**,但測試在產生 unknown 的那一刻就停了 —— **沒有人問「然後呢」**。#1/#3/S1 全是死在這個「然後呢」。 + +--- + +## 根因(一句話) + +**這個 codebase 把「查不到 / 對不上 / 沒設定」一律當成合法的空值往下傳(`unknown` / `[]` / `None` / placeholder / dry_run),而不是當成需要有人知道的異常 —— 每一層都盡責地 fail-safe 不炸,於是整條鏈路一路綠燈地什麼都沒做。** + +fail-safe 是對的(掃描器不該因為記 log 失敗而掛掉);錯的是 **fail-safe 沒有配一個「告訴人類」的出口**。三次被咬 + 這次抓到的兩個,全部符合這個公式: + +> **靜默失敗 = fail-safe(對的) + 零告警(錯的) + 只測有利方向(所以沒人發現)** + +**制度上的建議(比修這兩個 bug 更重要)**:立一條規矩 —— +**任何 `except` / 預設值 / `unknown` / 空清單的分支,如果它代表「本來該做的事沒做成」,就必須留下一個人看得到的痕跡**(print 至少,涉及錢/交付的一律 ntfy)。 +現有程式碼裡「靜默」與「有 log」的比例大約是:`weekly_report_v2` 的降級**有** degrade 卡(對 ✓)、`append_gauge_history` 失敗**有** print(對 ✓)、但 **tier=unknown 排除訂閱者(S1)、sku=unknown 發 placeholder(S2)這兩個直接關係到「收了錢有沒有給東西」的,反而完全無聲**。 diff --git a/docs/ecommerce/REDESIGN_SPEC_business.md b/docs/ecommerce/REDESIGN_SPEC_business.md new file mode 100644 index 0000000..2c2c853 --- /dev/null +++ b/docs/ecommerce/REDESIGN_SPEC_business.md @@ -0,0 +1,158 @@ +# 量化阿森電商 v2 — 商品線 + 定價重規劃規格(商業篇) + +> 版本:v2 商業規格 | 撰寫日:2026-07-16 | 定位:把台股數據管線(國際稀缺的護城河)包成可持續變現的數位商品組合,**以訂閱週報為旗艦**。 +> 誠信紅線(生死線):寫進任何商品的每一個具體數字都必須綁真實來源欄位(fail-closed),定位「歷史數據體檢,介紹≠推薦」,不喊單、附免責。 +> 本規格所有引用的資料檔路徑都經 Test-Path / 實讀樣本驗證存在(見附錄 A 盤點)。 + +--- + +## 0. 一句話商品線 + +**旗艦=「台股全市場週報」訂閱制(NT$99/149 雙層月費,Portaly 訂閱牆)**,由三層一次性 SKU(免費磁鐵→NT$99 tripwire→NT$990~1280 core 數據包)在前面漏斗導流,TradingView 30% 終身返佣為被動輔助收益。一次性 SKU 不再各自為政,每個都標明「爬到訂閱」的階梯關係。 + +--- + +## 1. 真實數據資產盤點(規格的地基,全部驗過) + +| 資產 | 路徑 | 覆蓋/規模(實測) | 關鍵欄位 | 更新頻率 | v2 用途 | +|------|------|------|------|------|------| +| 全市場自適應回測 | `twdata/adaptive_per_stock.csv` | 1770 檔 | code,name,market,bars,trend_frac,**a_net,a_pf,a_dd,a_tr,a_win,a_rdd**,t_net,t_pf,t_dd,t_tr,t_rdd | 靜態(2026-06-12 跑) | Core 數據包主檔、週報「結構基準」 | +| 多空回測 | `twdata/longshort_per_stock.csv` | 1770 檔 | code,name,market,bh,**l_net,l_pf,l_dd,l_tr,l_rdd,ls_net,ls_pf,ls_dd,ls_tr,ls_rdd,short_trades** | 靜態 | Core 數據包(併入,補做空維度) | +| 回測明細(含風險比) | `twdata/per_stock_results.csv` | 3682 列 | code,ticker,market,name,bars,start,end,net_profit_pct,profit_factor,max_dd_pct,n_trades,win_rate_pct,return_over_maxdd,**sharpe,final_equity** | 靜態 | Core 數據包(補 sharpe/起訖日/最終權益) | +| 全市場強弱掃描 | `quant-service/data_hunter/state.json` | universe 1925、wave_top **1001 檔**、sectors **34**、strong/weak 各 8、signals(long/short)、**track 真實追蹤成績** | gauge(temperature/breadth/adr/nhnl/avg_rsi)、sectors(score/bull_pct/leader/inst_count)、strong/weak(score/rsi/spark/ohlc)、signals.long/short、watch_long、wave_top、chips(foreign_top/trust_top/consec_top/margin_top/retail_exit_top)、track(n_closed/win_rate/avg_r/long_win_rate/short_win_rate/recent) | **每日**(最新 2026-07-16) | **旗艦週報主體** | +| 個股深度體檢事實庫 | `youtube_channel/STUDIO/stock_checkup_facts.json` | by_code **僅 9 檔**、results 104 則、**11 種 fact 類型/檔** | 每則 fact:key/claim/method/**source**/period;類型:long_horizon,annual_extremes,three_way,underwater,halvings,crash(2008/2020/2022),revenue_trend,eps_trend,gross_margin,dividend_history,valuation_position | 每日 cron 累積(慢) | 週報「深度體檢層」、Core 體檢合輯 | +| 估值面 | `twdata/fundamentals/valuation_YYYYMMDD.json` | **1078 檔**/日(近 14 日) | 每 code:pe,dividend_yield,pb | 每日 | 週報「估值位階雷達」 | +| 基本面 | `twdata/fundamentals/stock_XXXX.json` | 135 檔 | eps_q,eps_ttm,eps_yoy,gross_margin,op_margin,rev,rev_yoy,rev_mom,cash_div,stock_div,div_year,ex_date | 按需 | 體檢/週報基本面補充 | +| 法人籌碼(日) | `twdata/chips/YYYY-MM-DD.json` | ~1898 檔/日、**21 個交易日** | foreign_net,trust_net,instinv_net | 每日 | 週報「法人週流向」(跨 5 日加總) | +| 融資券當沖(日) | `twdata/margin/YYYY-MM-DD.json` | ~1845 檔/日、**16 日** | margin_balance,margin_chg,short_balance,short_margin_ratio,day_trade_lots | 每日 | 週報籌碼補充 | +| 分級交易區 | `twdata/zones.json` | daytrade/swing/longterm 各 15 檔 | code,name,industry,price,chg,zscore,setups,metrics,play(entry/stop/target) | 每日 | 免費磁鐵/週報 swing 區 | +| 當沖適格 | `twdata/daytrade_eligibility_*.json` | disposition/attention 清單 | disposition[],attention[] | 每日(檔 <400B) | **免費磁鐵**(不再當付費 SKU) | + +### 盤點發現的資料資產風險(重要,直接影響商品可行性) +1. **體檢事實庫只覆蓋 9 檔**(2330/2317/2454/2603/2412/2882/00878/2408/2327),results 104 則。v1 週報以體檢為主體 → 一週餓死。**v2 已改用 state.json(1001 檔)當週報主體,體檢降為加值層。** 長期靠 `stock_checkup_daily` cron 累積覆蓋,覆蓋數是訂閱深度的成長曲線。 +2. **回測三檔(adaptive/longshort/per_stock)是 2026-06-12 靜態快照**,非即時。只能當「結構背景/教學基準」,商品文案**不得**宣稱即時或可交易訊號。 +3. **chips 僅 21 日、margin 僅 16 日** → 可算「本週法人流向」,但無法做長期籌碼趨勢;需持續累積。 +4. **valuation 覆蓋 1078 檔(非全 1925)**,且含 null(如 pe=null),渲染需濾空。 +5. **daytrade_eligibility 是每日小快照(<400B)**,賣成靜態商品隔天就過期 → v2 砍為免費磁鐵(每日重生)。 +6. **state.json 休市/熔斷時 signals 可能空**(實測 daytrade.json circuit_breaker tripped、signals=[]) → 週報排版需 fail-safe(有就列、無則跳過,不硬湊)。 + +--- + +## 2. 旗艦:「台股全市場週報」訂閱規格 + +### 2.1 產品定義 +- **名稱**:量化阿森 台股全市場週報(Carson Quant — Taiwan Whole-Market Weekly) +- **平台**:Portaly 訂閱牆(台灣)、Whop(國際實驗,第二階段) +- **交付**:每週一次完整週報(Email + Telegram 私訊),訂閱者另享每日掃描(daily bonus) +- **引擎**:`quant-service/ecommerce/subscription_report.py`(v2 重寫,見交付規格) +- **雙層**:基礎版 NT$99/月(§2.3 標 ★)、完整版 NT$149/月(全 section + 數據下載 + 深度體檢) + +### 2.2 誠信結構(每個 section 都綁來源) +週報引擎**不產生任何新數字**:所有數值一律逐字引用來源檔既有欄位/claim 字串;缺 source/claim/data 的事實由 `_fact_ok` fail-closed 濾除(沿用 v1 `subscription_report._fact_ok`,不自造弱化版)。全市場榜單數字直接來自 state.json 欄位,渲染層只做「取欄位→格式化」不做推論。 + +### 2.3 週報 Section 規格(7 個固定 + 1 個輪替) + +| # | Section | 資料來源檔:欄位 | 產出規則/公式 | 範例列(取自實檔) | 層級 | +|---|---------|------|------|------|------| +| S1 | 市場溫度與體質 | `data_hunter/state.json`:gauge.temperature,label,breadth,adr,nhnl,avg_rsi;index.trend,above_yearline | 直接陳述溫度與體質,不判斷方向。溫度=components 加權(rsi/breadth/adr/nhnl/vol) | 溫度 45.4(中性)|站上20MA 40.9%|漲跌比(ADR) 1.8|60日新高95/新低88|0050 趨勢 UP 站上年線 | ★基礎 | +| S2 | 板塊輪動熱力 | `state.json`:sectors[](name,avg_chg,bull_pct,score,count,leader,inst_count) | 34 板塊依 score 排序,列 Top5/Bottom5,附完整 34 板塊 CSV(可排序) | 貿易百貨業 score54.0 均漲+1.06% 多方63% 領漲「統領+10.0%」法人買15檔 | ★基礎 | +| S3 | 全市場強弱榜 | `state.json`:wave_top[](1001 檔:code,name,industry,price,chg,rsi,score,st)、strong/weak、ranks.up/down/amount/amplitude | 1001 檔依 score 排序取 Top30/Bottom30 進報告本體,**全 1001 檔附 CSV** 供 Excel 排序篩選(呼應「版面密可排序」) | 馬光-KY(4139)生技 +9.97% RSI87.6 score96.6 UP | ★基礎 | +| S4 | 法人與籌碼週流向 | `twdata/chips/YYYY-MM-DD.json`×本週5日:foreign_net,trust_net,instinv_net + `state.json`:chips(foreign_top/trust_top/**consec_top連買**/retail_exit_top/margin_top) | 對每 code 加總本週 5 個交易日 foreign_net → 排序;外資/投信連買天數取 consec_top | 2887 外資單日買 47478 張;投信連買榜、散戶提前下車榜 | 完整 | +| S5 | 估值位階雷達 | `twdata/fundamentals/valuation_YYYYMMDD.json`:pe,dividend_yield,pb(1078檔) | 濾 null 後,列全市場殖利率 Top20、本淨比 Bottom20、本益比分布(P25/中位/P75);只陳述位置不判斷貴賤 | 1108 殖利率7.19% PE6.88 PB1.04;全市場 PE 中位數(當期算出) | 完整 | +| S6 | 訊號追蹤 · 誠實成績單 | `state.json`:track(n_closed,win_rate,avg_r,avg_ret_pct,long_win_rate,short_win_rate,recent[]) | 直接亮**真實追蹤戰績**(含輸單),不挑不藏。這是誠信紅線的**正面武器**與差異化(對比只曬贏單的 guru) | 已平倉19筆 勝率X% 平均R值X 多方勝率/空方勝率 + 近期逐筆 | ★基礎(招牌) | +| S7 | 本週深度體檢個股 | `youtube_channel/STUDIO/stock_checkup_facts.json`:results(依 computed_at 落在本週窗)、11 種 fact 的 claim/source | 逐字引用體檢 claim(長期含息報酬/套牢期/腰斬/崩盤三段/毛利/股利/估值位階…),每則附 source | 台積電:近20年含息總報酬8728.8%(年化25.1%,最大回撤-46.5%);史上最長套牢10.7年 | 完整(深度) | +| S8 | 結構基準(輪替/月度) | `twdata/adaptive_per_stock.csv`+`longshort_per_stock.csv` | 每月輪替一次教育性基準:全市場 adaptive 淨報酬中位數、正報酬佔比,教「中位數優先」思維(不被最好幾檔騙) | 全市場 1770 檔 adaptive 中位數淨報酬(當期算出)、正報酬佔比 | 完整 | + +> **每日 bonus(訂閱者專屬)**:`generate_daily_scan()` 續用,吃 state.json 當日掃描(溫度/板塊/多空訊號/法人),復用 `data_hunter/daily_post.py` 排版 helper。 + +### 2.4 更新頻率與依賴 +- 週報:每週一產出(排程),吃當週最新 state.json + chips 5 日 + valuation 最新日 + 本週新完成體檢。 +- 依賴:state.json 每日掃描已在跑(local_cron);chips/valuation cron 已在跑;體檢覆蓋隨 stock_checkup_daily 成長。 + +--- + +## 3. 一次性 SKU 重新設計(砍/留/加,全部標明與訂閱的階梯) + +> 原則:一次性 SKU 是**漏斗**不是終點。免費磁鐵抓名單 → tripwire 建立付費習慣 → core 服務「不想訂閱只要一份」的買家 → 全部導向訂閱(「這份,但每週更新+真實追蹤」)。 + +### 階梯 L0 — 免費磁鐵(抓 Email/TG,不收費) +| SKU | 中/英名 | 內容物 | 資料來源 | 目標客群 | 與訂閱關係 | +|-----|---------|--------|----------|----------|-----------| +| M1 | 台股當沖適格清單 / TW Day-Trade Eligibility List | 當日處置股/注意股清單 + 盤前防呆 5 點 | `daytrade_eligibility_*.json`(每日重生,不賣過期) | 當沖/短線新手 | 落地頁換 Email → 週報試閱 | +| M2 | 單檔旗艦體檢報告(台積電) / Single Flagship Health-Check | 2330 的 11 項體檢完整版(PDF) | `stock_checkup_facts.json`:results__2330 | 存股/長線 | 免費嚐鮮 → S7 深度體檢是訂閱常態 | + +### 階梯 L1 — Tripwire(建立付費習慣,低價衝動購買) +| SKU | 中/英名 | 內容物 | 資料來源:欄位 | NT$/US$ | 平台 | 與訂閱關係 | +|-----|---------|--------|------|------|------|-----------| +| T1 | 台股定投追蹤模板 / TW DCA Tracker | Excel/CSV 定投模板 + 10年真實對照(All-in vs 定投 vs 0050) | `stock_checkup_facts.json`:checkup_three_way(stock_allin/stock_dca/bench.total_return) | 99 / $5 | 蝦皮·Gumroad | 買家=長線族 → 推 S7/S8 訂閱 | +| T2 | 個股體檢單檔報告(自選權值股) / Single-Stock Health-Check | 任一已覆蓋權值股(9檔可選)的 11 項體檢 PDF | `stock_checkup_facts.json`:results__{code} | 149 / $7 | 蝦皮·Gumroad | 「想每檔都有?訂週報」 | + +### 階梯 L2 — Core(一次性高值數據包,服務不想 recurring 的買家) +| SKU | 中/英名 | 內容物 | 資料來源:欄位 | NT$/US$ | 平台 | 與訂閱關係 | +|-----|---------|--------|------|------|------|-----------| +| C1 | 台股全市場回測數據包 / TW Full-Market Backtest Pack | 1770 檔合併 CSV(adaptive+多空+sharpe/起訖/最終權益)+ 摘要 PDF | `adaptive_per_stock.csv`+`longshort_per_stock.csv`+`per_stock_results.csv`(a_net,a_pf,a_dd,a_win / ls_net,short_trades / sharpe,final_equity) | 990 / $35 | Portaly·Gumroad | 一次性;訂閱=「每週更新版」 | +| C2 | 台股權值股體檢合輯 / TW Blue-Chip Health-Check Bundle | 已覆蓋權值股全體檢合輯(隨覆蓋成長)PDF+摘要 | `stock_checkup_facts.json`:全 by_code × 11 fact | 1280 / $39 | Portaly·Gumroad | core 買家 → S7 每週新增體檢 | + +### 砍掉的 v1 SKU(說明理由) +- **daytrade_checklist(付費版)** → 砍。賣「當日快照」靜態檔隔天過期,誠信與實用雙輸。改為免費磁鐵 M1(每日重生)。 +- **scan_sop(選股SOP純文字)** → 砍。無數據、薄;內容併入訂閱 onboarding 首封信。 +- **intl_* 全英版一次性**(v1 每類都做英版)→ 收斂。國際只保留 C1/C2 的英版(Gumroad)+ 訂閱英版(Whop 實驗),不再每個 SKU 都出雙語,降維護成本。 + +--- + +## 4. 定價階梯(NT$ / US$ + 定價邏輯) + +### 4.1 完整價格表 +| 層 | 商品 | NT$ | US$ | 平台 | 定價邏輯 | +|----|------|-----|-----|------|----------| +| L0 磁鐵 | M1 當沖清單 / M2 單檔體檢 | 0 | 0 | 落地頁 | 抓名單,0 摩擦 | +| L1 tripwire | T1 定投模板 | 99 | 5 | 蝦皮/Gumroad | 對齊 Gumroad 數位小物 $5 心理價;NT$99 台灣衝動購買甜蜜點 | +| L1 tripwire | T2 單檔體檢 | 149 | 7 | 蝦皮/Gumroad | 略高於 T1,錨定「一檔=一杯咖啡」 | +| L2 core | C1 全市場回測包 | 990 | 35 | Portaly/Gumroad | 稀缺台股全市場數據,國際 $35 仍遠低於機構數據 | +| L2 core | C2 體檢合輯 | 1280 | 39 | Portaly/Gumroad | 深度>廣度,最高一次性價位 | +| **旗艦訂閱** | **週報 基礎版(★section)** | **99/月** | **9/月** | **Portaly/Whop** | 見下 | +| **旗艦訂閱** | **週報 完整版(全section+下載+深度體檢)** | **149/月** | **15/月** | **Portaly/Whop** | upsell,+50% 拿深度層 | +| 訂閱 年繳 | 完整版年繳 | 1290/年 | 129/年 | Portaly/Whop | ≈NT$107/月,省 28%,鎖 LTV | +| 聯盟 | TradingView 30% 終身 | — | recurring | 內嵌 | 被動,不佔漏斗主線 | + +### 4.2 定價邏輯與對照組(查證 2026-07-16) +- **Seeking Alpha Premium ≈ US$24.92/月**(US$299/年);**Seeking Alpha Pro US$200/月**。來源:[about.seekingalpha.com/premium-subscription-price-update](https://about.seekingalpha.com/premium-subscription-price-update)、[seekingalpha.com/subscriptions](https://seekingalpha.com/subscriptions) +- **Substack 財經電子報平均 ≈ US$30.6/月**,平台最低 $5/月,年繳常見 $50。來源:[readless.app/blog/best-paid-substack-newsletters-2026](https://www.readless.app/blog/best-paid-substack-newsletters-2026)、[support.substack.com](https://support.substack.com/hc/en-us/articles/360037607131-How-much-does-Substack-cost) +- **定價策略**:我方訂閱 US$9~15/月 = 對國際同類(SA $25 / Substack $30)**積極低價卡位**,理由=(1)台股全市場數據英文世界稀缺,但(2)頻道現況約 30 訂閱、信任尚未建立,land-grab 定價優先衝訂閱數與口碑。台灣端 NT$99~149/月,相對台灣財經 VIP 服務常見 NT$300~1000/月級距(一般市場認知,非單一 URL)明顯低價,對齊 Carson「先衝規模+誠信建立信任」策略。 +- **年繳邏輯**:省 28% 換 12 個月 LTV 鎖定,對抗訂閱早期高流失。 + +--- + +## 5. 上線順序(訂閱主柱優先) + +| 序 | 動作 | 平台 | 依賴 | 對外紅線 | +|----|------|------|------|----------| +| 1 | 週報引擎 v2 重寫 + 產一份**公開免費樣本週報**(證明價值) | 本機→落地頁 | state.json(已跑)、subscription_report v2 | 內容審(誠信驗證)後才公開 | +| 2 | L0 免費磁鐵 M1/M2 上落地頁 + Email/TG 抓名單 | 落地頁 + tg_magnet | 漏斗頁重做(Phase 2c) | opt-in 名單 | +| 3 | Portaly 訂閱牆設定(基礎/完整/年繳三檔)+ webhook v2 驗簽 + send_report 交付串接 | Portaly | webhook v2(Phase 2d)、PING/簽章欄位校準 | **動錢/新對外管道→先問 Carson** | +| 4 | L1 tripwire T1/T2 上蝦皮+Gumroad | 蝦皮·Gumroad | product_factory v2、Gumroad PING_TOKEN | 上架前誠信驗證 | +| 5 | L2 core C1/C2 上 Portaly+Gumroad | Portaly·Gumroad | product_factory v2 | 同上 | +| 6 | Whop 國際訂閱實驗(英版週報) | Whop | 訂閱引擎穩定後 | 新對外管道→先問 Carson | + +> 關鍵路徑:**序 1→3 是旗艦主柱**。一次性 SKU(序 4/5)可與訂閱並行,但資源優先給訂閱。所有真實對外發送(送信/上架/收款)一律先過 fresh-context 誠信驗證(Phase 3),有授權也不免驗。 + +--- + +## 6. v1 哪裡不夠好 → v2 怎麼改(對照) + +1. **旗艦模糊**:v1 訂閱與一次性平等對待、SKU 各自為政 → **v2 明確以訂閱週報為旗艦**,所有一次性 SKU 重新定位成漏斗階梯(磁鐵→tripwire→core→訂閱),每個標明爬升關係。 +2. **週報餓死**:v1 週報只吃體檢事實庫(僅覆蓋 9 檔)→ 一週沒幾條 → **v2 週報主體改吃 state.json 全市場掃描(1001 檔 wave_top+34 板塊+強弱榜+法人籌碼+真實訊號追蹤)**,體檢降為深度加值層,徹底解決覆蓋不足。 +3. **賣過期數據**:v1 把 daytrade_checklist「當日快照」賣成靜態商品(隔天過期)→ **v2 砍為免費磁鐵**(每日重生),不賣會壞掉的東西。 +4. **沒有誠實武器**:v1 無「成績單」→ **v2 把 data_hunter track 的真實追蹤勝率(含輸單)做成固定招牌 section**——把誠信紅線從「防捏造的守門」升級成「主動亮真實戰績」的差異化賣點(對比只曬贏單的 guru)。 +5. **版面不密不可排序**:v1 是散落 md → **v2 要求全市場榜單一律附完整 CSV**(1001 檔強弱、34 板塊可在 Excel/Sheets 排序篩選),報告本體給 Top/Bottom+分布,呼應 Carson「準則更專業+範圍更廣+版面更密可排序」品味。 +6. **數據包單薄**:v1 fullmarket 只用單一 adaptive csv → **v2 core C1 合併 adaptive+多空+per_stock 三檔**,補做空維度與 sharpe/起訖日/最終權益。 +7. **薄 SKU 佔位**:v1 scan_sop 純文字無數據 → **砍掉**,併入訂閱 onboarding。 +8. **無 recurring 主柱**:v1 全一次性 → **v2 建雙層月訂閱(NT$99/149)+ 年繳鎖 LTV**,才是可持續變現。 + +--- + +## 附錄 A:路徑存在性驗證(全部實測) +所有 §1 表列路徑均以 `ls`/Glob/`python json.load`/`head` 於 2026-07-16 實讀樣本確認存在且欄位如表所述。體檢庫 by_code 實測 9 檔(2330,2317,2454,2603,2412,2882,00878,2408,2327);valuation 最新檔 `valuation_20260716.json` 1078 檔;state.json wave_top 1001 檔、sectors 34;回測三檔各 1770/1770/3682 列。 + +## 附錄 B:對外/動錢紅線提醒 +訂閱牆收款、名單發送、平台上架皆屬「對外發布/動錢」紅線 → 執行前先問 Carson(新管道/動錢)且一律過 fresh-context 誠信驗證。product_factory v2 沿用 fail-closed 溯源守門,任何查無來源數字 → 該 SKU 整個中止不出檔。 diff --git a/docs/ecommerce/REDESIGN_SPEC_product.md b/docs/ecommerce/REDESIGN_SPEC_product.md new file mode 100644 index 0000000..409fd4f --- /dev/null +++ b/docs/ecommerce/REDESIGN_SPEC_product.md @@ -0,0 +1,296 @@ +# 量化阿森電商 v2 — 成品視覺 + 渲染管線規格(產品篇) + +> 對應任務:v1「做不夠好、全部重做」中的**成品視覺系統 + 渲染管線選型**。 +> 姊妹文件:`REDESIGN_SPEC_business.md`(商品線/定價/漏斗,由 spec-business 負責)。 +> 本文所有 token 皆已在 mockup 實跑驗證: +> `quant-service/ecommerce/mockup/subscription_weekly_sample.html`(+ `.pdf` + `render.py`)。 + +--- + +## 0. v1 現況(baseline,要超越的對象) + +| 面向 | v1 實況 | 問題 | +|---|---|---| +| 旗艦訂閱週報交付 | `subscription_report.py` **只吐純文字 `.md`**,email/telegram 純文字送出 | 旗艦商品沒有任何成品排版,像記事本,完全撐不起訂閱費 | +| 一次性 SKU 報告 | `product_factory.md_to_pdf` 用 **reportlab + STSong-Light**(淺灰底/office 排版) | 白底、無品牌、無圖表、無數據卡質感;與頻道暗色品牌完全脫節 | +| xlsx | `csv_to_xlsx` **裸傾印**:單一 `tracker` 工作表、只設欄寬 | 無凍結窗格/篩選/條件格式/表頭樣式,不像數據產品 | +| 圖表 | **完全沒有** | 純數字表格,無 sparkline / 位階 / 對照視覺 | +| 色彩 | 灰階 | 沒有台股漲紅跌綠、沒有暗金 accent | + +**baseline 誠信面(要保留、不可退化)**:v1 的溯源守門(`fact_source_guard` fail-closed)、 +「介紹≠推薦」、數字綁定來源欄位(`Provenance.num` / `_fact_ok`)是對的,v2 **只換視覺與渲染外殼, +誠信結構原封搬進來**(見 §7)。 + +--- + +## 1. 渲染管線選型(定案) + +### 定案:HTML + CSS → headless Chromium(Playwright)→ `page.pdf()` + +**已實跑驗證**:Chromium 148.0.7778.96 本機可啟動;`render.py` 產出 536 KB A4 PDF, +深色底真的印進去、繁中零缺字(見 mockup)。 + +### 為什麼不是別的 + +| 方案 | 判定 | 理由 | +|---|---|---| +| **reportlab(v1)** | ✗ 淘汰 | 手刻 flowable、無 CSS、深色底/圖表/數據卡幾乎不可能做到位;維護成本高 | +| **WeasyPrint** | ✗ 不用 | 純 CSS 引擎但不吃 flexbox/grid 的完整實作、SVG/漸層支援弱,做暗金質感會處處受限;還要另裝 GTK 依賴 | +| **matplotlib 直出整頁** | ✗ 不用 | 排版能力弱,做不出封面/數據卡/雙語版式 | +| **HTML→Chromium(定案)** | ✅ | 完整 CSS grid/flex、漸層、SVG、system 繁中字型;WYSIWYG(瀏覽器怎麼看就怎麼印);本機已裝 Playwright;和 web_center 前端同一套技術棧,可共用元件 | + +### 管線關鍵參數(已驗證,見 `render.py`) + +```python +pg.emulate_media(media="print") +pg.pdf( + prefer_css_page_size=True, # 尊重 .page 的 210mm×297mm,不被預設 A4 邊界干擾 + print_background=True, # ★ 深色底真的印進 PDF(不設 → 白底) + margin={"top":"0","bottom":"0","left":"0","right":"0"}, # 邊界改由 CSS .pad 管 +) +``` + +CSS 端必配: +```css +html{ -webkit-print-color-adjust:exact; print-color-adjust:exact; } /* 強制印背景色 */ +``` + +### 分頁策略(重要,決定「頁首頁尾/頁碼/分頁控制」怎麼做) + +**定案:顯式 A4 頁面 div(`.page { width:210mm; height:297mm; page-break-after:always }`)。** +每頁是一個固定尺寸容器,背景/邊框/頁首/頁尾/頁碼**逐頁烘進 DOM**,得到像素級可控、 +所見即所印。這是設計型 PDF 的專業做法,勝過「一長條讓 Chromium 自動分頁」。 + +- **頁首/頁尾**:每頁 div 內各放一個 `.runhead` / `.runfoot`(品牌 logo + 期號 + 頁碼)。 + 不用 Playwright 的 `header_template/footer_template`(它在獨立白底 context 渲染、 + 吃不到頁面 CSS、樣式受限,是已知痛點)。 +- **頁碼**:寫死在每頁 `.runfoot`(顯式分頁下本就知道第幾頁),不靠不可靠的 CSS `counter(page)`。 +- **動態長度內容的自動分頁**(真引擎需要,mockup 因內容固定是手排): + 在 Chromium 內用 JS 量測 `card.offsetHeight`,把資料卡依序塞進當前 `.page` 直到裝滿 + (超過可用高度就開新頁),再 `page.pdf()`。→ §5 演算法。 + +### 已知坑(實跑遇到 / 要留意) + +1. `print_background` **一定要開**,否則深色底整片變白(v2 命脈)。 +2. 深色底 PDF 檔案較大(mockup 3 頁 536 KB;滿版深色點陣紋理會加大)——可接受, + 若要壓可把 `.page::before` 點陣紋理 opacity 降低或改用更省的漸層。 +3. 繁中**必須明確指定** `font-family`,不能靠 Chromium 預設 fallback: + 本機已確認有 `Microsoft JhengHei`(msjh/msjhbd/msjhl)、`Noto Sans TC`、`Noto Serif TC`。 +4. `wait_until="networkidle"`:所有資產走 inline(SVG/漸層/data-uri),不依賴外網,離線可印。 +5. 圖表用 **inline SVG**(sparkline/位階條/對照橫條)——向量清晰、主題一致、可套 CSS 變數; + 只有「多點權益曲線/價格走勢」這種點多的才退回 matplotlib 暗色 PNG 嵌 data-uri(§4)。 + +--- + +## 2. 視覺 token(色票 / 字級 / 間距) + +### 2.1 色票(hex,已在 mockup 生效) + +**深色基底** +| token | hex | 用途 | +|---|---|---| +| `--bg` | `#0B0E14` | 主背景(近黑帶藍) | +| `--bg2` | `#0D1017` | 頁面漸層底 | +| `--card` | `#141922` | 數據卡表面 | +| `--card2` | `#1A2029` | 抬升卡面 | +| `--pod` | `#10151D` | 內嵌 pod / KPI 底 | +| `--line` | `#242C38` | 髮絲線(弱) | +| `--line2` | `#2E3745` | 邊框(強) | + +**文字** +| token | hex | 用途 | +|---|---|---| +| `--tx` | `#E6EAF0` | 主文(off-white,不用純白才高級) | +| `--tx2` | `#9AA5B5` | 次文 | +| `--tx3` | `#5D6675` | 說明/caption | + +**暗金 accent(頻道品牌色)** +| token | hex | 用途 | +|---|---|---| +| `--gold` | `#C9A227` | 暗金主色(eyebrow/標題強調) | +| `--gold-hi` | `#E3B93E` | 亮金(高亮/marker/sparkline) | +| `--gold-deep` | `#8A6D1C` | 深金(漸層底/分隔線) | + +**台股漲跌色(★ 慣例:漲/正=紅、跌/負=綠 —— 與西方相反,硬規)** +| token | hex | 用途 | +|---|---|---| +| `--up` | `#FF5C5C` | 漲/正報酬(紅) | +| `--dn` | `#33D69F` | 跌/負報酬(綠) | +| `--amber` | `#E3B93E` | 燈號中段 | + +> **3 個關鍵 hex**:背景 `#0B0E14`、暗金 `#C9A227`、台股紅 `#FF5C5C` / 綠 `#33D69F`。 + +**燈號(估值位階,只標位置、非買賣)**:綠 `#33D69F`(≤P60)/ 琥珀 `#E3B93E`(P60–95)/ 紅 `#FF5C5C`(≥P95)。 + +### 2.2 字體與字級階層 + +**字族** +- 內文/數據:`"Microsoft JhengHei","Noto Sans TC","Segoe UI",-apple-system,sans-serif` +- 封面大標(magazine 質感):`"Noto Serif TC",serif` +- 全域 `font-variant-numeric:tabular-nums`(數字等寬,表格/KPI 對齊) + +**字級(px,已驗證)** +| 角色 | size / weight / 其他 | +|---|---| +| 封面大標 masthead | 58 / 700 / Noto Serif TC,line-height 1.06 | +| eyebrow(小標籤) | 10–12 / letter-spacing .14–.34em / uppercase / 金色 | +| 區塊 H2 | 19 / 600 | +| 資料卡股名 | 18 / 700(code 11、mono、`--tx3`) | +| KPI 大數字 | 20–30 / 700 / tabular | +| 內文 | 11.5–12.5 / line-height 1.7 | +| 表格 cell | 11.5 / tabular | +| 說明/來源 | 9.5–10 / `--tx3` | + +### 2.3 版式與間距 + +- 頁面:`.page` 210×297mm;`.frame` 內縮 9mm 髮絲金框;`.pad` 內距 11mm 12mm 14mm。 +- 卡片圓角 8–11px、左緣 3px 金色漸層 bar(品牌記號)、`--line2` 邊框。 +- 背景層次:雙 radial(右上暗金光暈 10% + 左下冷藍 14%)+ 垂直漸層 + 極淡點陣紋理 + (`radial-gradient` dot,22px 間距,opacity .5)——質感但不喧賓。bloom 一律克制 + (box-shadow 發光 ≤ 10px、opacity ≤ .3),符合 Carson「暗才高級、發光克制」。 + +--- + +## 3. 數據卡元件規格 + +### 3.1 本期總覽密表(封面後第一頁,Carson 要的「密、可排序」) + +- 表頭:深色帶 `#0E141C`、金字 `--gold`、下緣 1.5px `--gold-deep`;每欄附排序提示符 + (`▼` / `A→Z`)標明**預設排序鍵**(視覺暗示可排序;真互動在 web_center,PDF 內是靜態快照)。 +- 每列:股名(粗)+ 代號(mono、`--tx3`)、年化報酬(台股紅綠)、最大回撤(綠)、 + **估值位階 cell**、最長套牢、殖利率。 +- 斑馬紋:偶數列 `rgba(255,255,255,.014)`(極淡)。 +- 數字欄一律右對齊 + tabular-nums。 + +### 3.2 紅綠燈號 + 估值位階條(誠信核心元件) + +**估值位階不用「買賣燈」,用「位置條」**——這是把「介紹≠推薦」做進視覺: +- 燈號 dot 只標**落在自身近 10 年區間的哪一段**(綠≤P60 / 琥珀 P60–95 / 紅≥P95), + 文字寫「P98 偏高」而非「貴/該賣」。 +- 位階條:track(`--line`)+ 淡金 IQR 帶(P25–P75)+ 金色 marker(目前本益比)+ + 中位刻度;下方標 `P25 / 中位 / P75` 實際倍數。純位置陳述。 + +### 3.3 sparkline(營收/EPS/毛利率趨勢) + +- **inline SVG**:`viewBox` 正規化;`polyline` 金線(`--gold-hi`,1.6px,round join)+ + 面積填充 `url(#gf)`(金 28%→0% 垂直漸層)+ 末點 2.6px 金點。 +- 右側 meta:最新值(大字)、起點年/值、區間變化(台股紅綠)。 +- 真引擎:series 取事實庫既有年度序列;若只有端點,mockup 用示意序列並在免責標「示意序列(端點為真實值)」。 + +### 3.4 三種買法對照(All-in vs 定投 vs 0050) + +- 三條橫 bar,寬度 = 各自報酬 ÷ 該組最大值(同基準才可比): + All-in 金漸層 / 定投 灰 / 0050 藍。右側數值台股紅綠。一眼看出「單筆 vs 定投 vs 大盤」差距。 + +### 3.5 崩盤韌性 pod(2008/2020/2022) + +- 三個等寬 pod:年度標籤、跌幅(**綠**,因下跌)、「抱到今 +X%」(**紅**,因正報酬)。 + 台股色規則貫徹到底。 + +### 3.6 KPI mini-tile + +- `.kpi`:pod 底、上標(uppercase caption)+ 大數字(台股紅綠)。卡頭右側橫排 2–3 顆 + (年化 / 最大回撤 / 最長套牢 或 殖利率)。 + +--- + +## 4. 圖表路線(SVG 優先,matplotlib 備援) + +| 圖種 | 做法 | 理由 | +|---|---|---| +| sparkline、位階條、對照橫條、KPI | **inline SVG / CSS** | 點少、向量清晰、吃 CSS 變數主題一致、檔案小、可印可縮放 | +| 多點權益曲線 / 還原價格走勢 / 相關熱圖 | **matplotlib 暗色 PNG → data-uri 嵌入** | 點多時手刻 SVG path 不划算;matplotlib 出 2x DPI 深色圖較省 | + +**matplotlib 暗色輸出約定**(要與 token 對齊): +```python +plt.rcParams.update({ + "figure.facecolor":"#0B0E14","axes.facecolor":"#10151D", + "text.color":"#9AA5B5","axes.edgecolor":"#242C38", + "xtick.color":"#5D6675","ytick.color":"#5D6675","axes.grid":True, + "grid.color":"#242C38","font.family":"Microsoft JhengHei", +}) +# 漲紅跌綠:漲段 #FF5C5C、跌段 #33D69F、主線 #E3B93E;dpi=200 存 PNG → base64 → +``` + +--- + +## 5. 動態分頁演算法(真引擎,mockup 因固定內容手排) + +``` +可用高度 H = 297mm − 上下 pad(≈ 259mm)− runhead − runfoot +current_page = 新 .page(含 runhead/runfoot) +for card in 資料卡序列: + 量測 card.offsetHeight(在同寬容器內先 render 於離屏) + if 已用高度 + card 高 > H: + current_page 收尾;開新 .page(頁碼+1,重畫 runhead/runfoot) + append card 到 current_page;累加高度 +封面、總覽、免責頁為固定模板,前後各佔整頁 +``` +> 在 Playwright 內 `page.evaluate()` 跑量測即可,不需外部排版引擎。 + +--- + +## 6. xlsx 儀表板規格(openpyxl) + +**設計原則(誠實的人因取捨)**:PDF 是**成品展示** → 全深色高級感; +xlsx 是**使用者要編輯/篩選/列印的工作檔** → **深色表頭 + 淺色斑馬內文 + 台股紅綠條件格式**。 +全深色試算表難編輯難列印,故 xlsx 不照抄 PDF 的全暗;此為 ergonomic 取捨,已載明。 + +**多工作表結構** +1. `總覽`(dashboard):覆蓋個股 × 關鍵欄(年化/回撤/估值位階/殖利率),條件格式儀表。 +2. `個股體檢`:逐檔完整事實列(可篩選)。 +3. `全市場排行`:1770 檔回測(接 product_factory 一次性 SKU)。 +4. `定投對照`:All-in vs 定投 vs 0050 參考列。 + +**逐項規格(openpyxl 能力)** +| 項目 | 實作 | +|---|---| +| 深色表頭 | `PatternFill(start_color="0B0E14", fill_type="solid")` + `Font(color="E3B93E", bold=True)`;`row_dimensions[1].height=28` | +| 凍結窗格 | `ws.freeze_panes = "B2"`(凍表頭 + 首欄股名/代號) | +| 自動篩選 | `ws.auto_filter.ref = ws.dimensions` | +| 條件格式(台股色) | 報酬/勝率欄用 3 色階 `ColorScaleRule` **低=綠 `2FB877` → 中 `F2F2F2` → 高=紅 `E5484D`**(高報酬=紅,對齊台股);回撤欄反向 | +| 燈號 | 估值位階欄 `IconSetRule('3TrafficLights1')`(或自訂 dot 字元著色) | +| data bar | 淨報酬/成交量等量級欄 `DataBarRule(color="E3B93E")` | +| 數字格式 | 百分比 `0.0"%"`;金額 `#,##0`;代號**留字串**(前導零 0050/00878 不可被吃成數字) | +| 斑馬內文 | 偶數列淡灰 `PatternFill("F4F6F9")` | +| 欄寬 | 依內容量身(股名寬、數字欄窄),上限 40 | + +--- + +## 7. 誠信結構原封搬進 v2(不可退化) + +v1 的誠信是對的,v2 **只換皮不動骨**: +- `product_factory.Provenance.num()`:數字仍綁來源欄位、寫 `_provenance.json`;v2 渲染 + 只是把同一批已綁定的數字排進 HTML,**不新增任何手打統計**。 +- `fact_source_guard` fail-closed gate 保留;`subscription_report._fact_ok`(缺 source/claim/data 丟掉)保留。 +- **視覺化元件不得製造新語意**:估值用「位置條」不用「買賣燈」;崩盤/報酬只呈現既有數字; + sparkline series 來自事實庫,端點為真實值,插值一律標「示意」。 +- 每頁 runfoot + 免責頁固定帶「介紹 ≠ 推薦」與資料來源(FinMind / Yahoo 含息還原)。 + +--- + +## 8. v1 → v2 升級對照(核心,≥5 點) + +| # | v1 | v2 | 升級點 | +|---|---|---|---| +| 1 | 旗艦訂閱週報**只吐純文字 `.md`**(email/tg 純文字) | **品牌化深色 PDF**:封面 / 本期總覽密表 / 個股資料卡 / 免責 四段式 | 旗艦成品從「記事本」→「值得訂閱費的數據刊物」 | +| 2 | reportlab + STSong-Light 淺底 office 排版 | **HTML+CSS → Chromium**,全 token 化、深色印底、繁中 Microsoft JhengHei / Noto Serif TC 零缺字 | 渲染引擎換代,設計自由度與品牌一致性 | +| 3 | 灰階、無品牌 | **暗金鎖色 + 台股漲紅跌綠 + 燈號**,對齊頻道「暗色數據卡 + 金箭頭」 | 品牌識別落進成品 | +| 4 | 逐檔長條列點 | **本期總覽密表**(年化/回撤/估值位階可排序欄 + 燈號 + 位階條) | Carson 要的「密、可排序、資訊密度高」 | +| 5 | xlsx 裸傾印單表 | **多工作表儀表板**:凍結窗格 / 自動篩選 / 台股紅綠條件格式 / 深色表頭 / icon 燈號 / data bar | 從 CSV 傾印 → 專業級可用試算表 | +| 6 | **無任何圖表** | inline SVG **sparkline / 位階條 / 三種買法對照橫條**(+ matplotlib 暗色備援) | 從純數字 → 一眼可讀的視覺數據 | +| 7 | 誠信只在文字 | 誠信**視覺化**:估值用「位置條」非買賣燈、崩盤只呈現既有數字、插值標示意 | 「介紹≠推薦」紅線內建進設計元件 | + +--- + +## 9. 交付物索引 + +- 規格(本文):`docs/ecommerce/REDESIGN_SPEC_product.md` +- mockup 樣張(HTML):`quant-service/ecommerce/mockup/subscription_weekly_sample.html` +- mockup 渲染 PDF(實跑產出,536 KB,深色底 + 繁中零缺字): + `quant-service/ecommerce/mockup/subscription_weekly_sample.pdf` +- 渲染管線最小可行版(可直接被 v2 引擎 import 復用): + `quant-service/ecommerce/mockup/render.py` + +> Phase 2a(訂閱週報引擎 v2 實作)可直接把 `subscription_report.py` 的 `generate_weekly_report` +> 輸出改組成本規格的 HTML(用同一份 token + `render.py`),即完成旗艦成品升級。 diff --git a/docs/ecommerce/REVIEW_weekly_value.md b/docs/ecommerce/REVIEW_weekly_value.md new file mode 100644 index 0000000..b73fe1d --- /dev/null +++ b/docs/ecommerce/REVIEW_weekly_value.md @@ -0,0 +1,142 @@ +# REVIEW_weekly_value — 旗艦訂閱週報「值不值 NT$149/月」評審 + +> 評審人:verifier-sku2(fresh-context,未參與週報實作)|日期:2026-07-16 +> 立場:**付費者視角,不是工程視角**。前面的驗證都在問「數字對不對」(答案:對,零造假);這份問的是**「有沒有人願意每月掏 149」**。 +> 對象:`weekly_2026-07-16_full.pdf`(完整版)/ `_basic.pdf` / `weekly_2026-07-16_全市場數據.xlsx` +> 唯讀:未改程式/成品。對照組經**獨立上網複查**(URL 與日期見 §3)。 + +## 總判定(先講結論) + +**現在這個東西不值 NT$149/月。** 誠信面是滿分、數字零造假——但**誠信是入場券,不是賣點**。 + +8 段裡只有 **S7 深度體檢(8分)** 是「別處拿不到」;S4/S2/S3 是「別處做得更好更即時」;**S8 每週一模一樣**;**S6 招牌自己證明訊號會賠錢**;S1 是免費看盤軟體都有的天氣預報。 +訂戶付 149 買到的**唯一不可替代價值 = S7 的 7 檔隨機體檢**——而且他想看的那檔可能永遠抽不到。 + +**最小補救集合(做完才談得上 149)**:①修空白頁+名稱缺失 ②S8 每月真重跑或拿掉 ③**S7 開放點播** ④S1 加「溫度→歷史動作」對照 ⑤S6 重新定位成信任錨。 +**若只能做一件事:S7 點播。** 那是唯一的護城河。 + +--- + +## 1. 逐 section 價值評分(問:台灣散戶讀完會做出什麼他本來不會做的判斷?) + +| # | Section | 層 | 分數 | 可行動洞見是什麼 / 沒有的話缺什麼 | +|---|---|---|---|---| +| S1 | 市場溫度與體質 | basic | **4** | **幾乎沒有**。「溫度 45.4 中性」讀完不會改變任何動作——它只說今天天氣,不說要不要帶傘。漲跌家數/RSI 免費看盤軟體都有。**缺**:溫度→動作的歷史對照(「溫度<30 時,未來 20 日 0050 上漲機率 X%,樣本 N 次」)。這是把 4 分變 7 分最快的一刀,而且你手上有全市場歷史資料做得出來。 | +| S2 | 板塊輪動熱力 | basic | **6** | 有方向感(錢往貿易百貨/橡膠跑),散戶可據此換股。**缺**:輪動有沒有延續性的驗證——「上週最強板塊,本週續強機率?」沒有這個,榜單只是後照鏡。 | +| S3 | 全市場強弱榜 | basic | **6** | **xlsx 全量 1001 檔可自行排序篩選是真價值**(免費選股器多半不給匯出)。**缺**:榜單本身沒有 edge 宣稱,強弱分數的算法未揭露 → 買家不知道該不該信這個排序。 | +| S4 | 法人與籌碼週流向 | full | **7** | **台灣散戶最吃這套**,且「連買 10 日」是會被直接拿去下單的訊號。外資淨買賣 TOP 有可行動性。**缺**:CMoney 籌碼K線(≈NT$299/月)做得更即時、有圖、能查個股——你是週頻 PDF,結構性落後。 | +| S5 | 估值位階雷達 | full | **5** | 殖利率 Top15 對存股族是直接清單。**但有風險**:榜單前段幾乎全是營建股(新美齊 13.99%/皇普 13.54%/隆大 13.26%/華友聯/愛山林),這是典型**高殖利率陷阱**(一次性業外、建案認列完就掉),報告**只列榜、零警語**。對「怕被割的小白」這個受眾定位來說,**這一段目前是危險的**,不只是沒用。 | +| S6 | 訊號追蹤·誠實成績單 | basic | **5** | 誠信 10 分、商業 2 分。**它證明了自家訊號會賠錢**:19 筆已平倉、勝率 15.8%、avg_r -0.61、平均報酬 -7.77%。訂戶讀完的合理反應是「那我付 149 幹嘛」。**缺**:①n=19 樣本太小卻呈現得像結論,該標「樣本不足,不可外推」②應重新定位成「**這就是我們不賣訊號、不喊單的原因**」的信任錨,而不是當成績單展示。 | +| S7 | 本週深度體檢個股 | full | **8** | **全報告唯一的護城河**。「台積電史上最長套牢 10.7 年」「單筆 All-in 1746.9% vs 每月定投 669.2% vs 同期 0050 751.5%」「2008 崩盤 -43.8%,抱到現在 +6414.2%」——這些**會真的改變行為**(理解定投與單筆的真實差距、對套牢期有心理準備),而且**台灣沒有第二家免費給你這個**。 **缺**:訂戶不能點播想看的股票 → 見續訂殺手 #3。 | +| S8 | 結構基準(月度輪替) | full | **3** | 洞見本身很好(「趨勢策略無腦套全市場只有 53.1% 正報酬、中位數才 6.2%」是紮實的觀念矯正,且反著自己利益講)。**但它是靜態快照,每週一模一樣** → 見續訂殺手 #1。標「月度輪替」而資料凍結在 2026-06-12(**34 天未動**),名實不符。 | + +**平均 5.5/10。** 分佈很說明問題:**唯一 8 分的(S7)在 full;basic 四段最高 6 分**。 + +--- + +## 2. 三個價位的判定 + +### basic NT$99 — **太貴(該砍掉或降到 49)** +basic = S1(4)+S2(6)+S3(6)+S6(5)。**沒有任何一段是「別處拿不到」**:溫度/漲跌家數/板塊排序/強弱榜,免費看盤軟體與選股器都有;唯一獨特的 S6 還是在告訴你訊號會賠錢。 +→ **真正的風險不是「basic 太好導致沒人升級」,而是「沒人會買 basic」**。 +**建議**:要嘛廢掉 basic 專推 149;要嘛降到 NT$49 當入門鉤子,並**把 S7 的 1 檔放進 basic** 讓人嚐到護城河的味道(現在 basic 完全嚐不到)。 + +### full NT$149 — **價差成立,但絕對值目前不成立** ++50 元買到 S4(7)+S5(5)+S7(8)+S8(3)。**光 S7 就撐得起這 50 元**,價差邏輯是對的、upsell 動機清楚。 +但「149 相對 99 值」≠「149 本身值」。以現況(空白頁、名稱缺失、S8 重複、S7 樂透),**149 撐不住第 3 個月**。 + +### 年繳 NT$1290(≈107/月,省 28%)— **折扣合理,但現在不該推** +28% 折扣比對照組大方(MacroMicro 年繳省 17%)。問題不在數字,在**時機**: +產品目前的續訂殺手(S8 每週重複、S7 樂透)會在**第 2–3 個月**暴露,而年繳把不滿意的客戶**鎖 12 個月** → 換來的是退款糾紛與負評,對一個只有 ~30 訂閱、正在建立信任的頻道是**負資產**。 +**建議**:先只賣月繳,累積 3 個月真實 retention 數據再開年繳。年繳是獎勵已驗證的滿意度,不是用來提前鎖住未驗證的產品。 + +--- + +## 3. 對照組複查(獨立上網,2026-07-16 查) + +| 規格宣稱 | 複查結果 | 證據 | +|---|---|---| +| Seeking Alpha Premium ≈ **US$24.92/月**(US$299/年) | **✅ 屬實** | $299/年 retail 經多來源確認,$299÷12 = $24.92。[seekingalpha.com/subscriptions](https://seekingalpha.com/subscriptions)、[howthemarketworks.com](https://www.howthemarketworks.com/advanced/charts-and-patterns/seeking-alpha-discount/)(Retail Cost: $299 a year)、[ryanoconnellfinance.com](https://ryanoconnellfinance.com/partner_discounts/seeking-alpha-premium-discount/)(regular $299) | +| Substack 財經電子報平均 ≈ **US$30.6/月** | **❌ 誤引(高估約 2–6 倍)** | 規格引的**正是這個來源**,而該文現在明講:「Paid Substack newsletters cost between **$5 and $20 per month** in 2026, with the large majority clustering between **$5–15/month**」。該文標題是 *Best Paid Substack Newsletters 2026 (**Top 15 by Revenue**)* → $30.6 應是**營收前 15 名**的均價(倖存者偏誤子集),**不是「財經電子報平均」**。Substack 官方 going-paid 頁的預設級距也是 $5/$7/$10/$15/$30/$75。[readless.app](https://www.readless.app/blog/best-paid-substack-newsletters-2026)、[substack.com/going-paid](https://substack.com/going-paid) | + +**這個誤引會反噬定價結論**:規格說「我方 US$9~15 = 對國際同類(SA $25 / Substack $30)**積極低價卡位**」。但對真實 Substack 分佈($5–15,主群集 $5–15),**US$15 是天花板而非低價卡位**。國際版(Whop)訂價邏輯的地基有一半是錯的 → 建議國際版重訂在 **US$7–9**,或改以 SA($24.92,屬實)為唯一錨點並拿掉 Substack 那條。 + +### 台灣真正的競品(規格完全沒查,只寫「一般市場認知」) + +| 競品 | 價位 | 給什麼 | +|---|---|---| +| **財經M平方 MacroMicro** PRO | **USD 200/年(≈NT$533/月)** 或 USD 20/月(≈NT$640/月) | 上萬筆總經數據、每月 6–8 篇獨家報告、回測工具、ETF 篩選 | +| MacroMicro Prime / Max AI | USD 25 / USD 30 月繳(≈NT$800 / 960 月) | +產業決策平台、美股財報庫、AI 助理 | +| **CMoney 籌碼K線** APP 年訂閱 | ≈**NT$3,590/年(≈NT$299/月)** | 即時籌碼、主力動向、VIP 社團 | + +來源:[macromicro.me/subscribe](https://www.macromicro.me/subscribe)(2026-07-16 實抓)、CMoney 官方產品頁/FB 貼文(NT$3,590 年訂閱;未逐一核對當前頁面,**以官方頁為準**)。 + +**結論**:規格說「台灣財經 VIP 常見 NT$300~1000/月」→ **實查成立**(MM 533–640、CMoney ≈299)。**NT$149 的定位是對的**——約 MacroMicro 的 1/4、CMoney 的 1/2。 +**但定位對 ≠ 價值夠**:M平方是總經、CMoney 是即時籌碼,你是**台股個股層級的週頻數據**,三者不直接對打——這代表你**沒有價格參考點可以搭便車**,買家會純粹用「這 149 換到什麼」來評判。而目前換到的,主要是 S7。 + +--- + +## 4. 續訂殺手清單(最重要,按嚴重度) + +### 🔴 #1 S8 每週一模一樣 —— 訂戶第 2 週就會發現 +`twdata/adaptive_per_stock.csv` 最後修改 **2026-06-12**,今天 07-16,**34 天沒動**。S8 全段(1770 檔、中位數 6.2%、正報酬 53.1%、Top5 青雲/台光電/威致)**每週渲染出完全相同的數字**,而段落標題卻寫「**月度輪替**」。 +→ 一個付 149 的訂戶連看 4 週,會發現八分之一的內容是複製貼上。**這是最快、最確定的退訂觸發器。** +**怎麼補**:①真的每月重跑回測(產線已有,只差排程)②或改成「每月換一個基準主題」(這個月看趨勢策略、下個月看定投、再下個月看存股)③或直接從週報拿掉,改當 C1 數據包的加值,別在訂閱裡佔版面裝新鮮。 + +### 🔴 #2 S6 招牌自己證明訊號無效 —— 越誠實越勸退 +15.8% 勝率、avg_r -0.61、平均報酬 **-7.77%**、n=19。誠信上這是全報告最可敬的一段(**含輸單、不挑不藏**,台灣財經內容幾乎沒人敢這樣做);但商業上,訂戶讀完的第一反應是「**我在付錢訂閱一個會賠錢的訊號**」。 +→ 注意這一段在 **basic** —— 也就是**免費試閱/最低價層的門面就是自曝其短**。 +**怎麼補**:①**重新定位**:標題從「誠實成績單」改成「**為什麼我們不賣訊號**」,把負績效當成「所以這份報告只賣數據、不喊單、不報明牌」的信任錨——這樣它從勸退變成差異化 ②標明 **n=19 樣本不足、不可外推**(現在呈現得像結論)③若訊號長期為負,**誠實地停掉訊號段**比繼續每週展示更好。 + +### 🟠 #3 S7 是樂透 —— 唯一的護城河卻抽不到自己要的 +S7 每週給 7 檔(管線 1 檔/日),**訂戶不能指定**。backlog 1925 檔依成交值排序(下 25 檔:華邦電/聯電/群創/欣興/台達電/力積電/日月光/廣達/大立光/緯創…**都是好名字,約 10 個月內不會爛尾**——這點是好消息)。 +但問題是:**我持有的那檔,可能要等好幾個月甚至幾年才輪到**。全部輪一遍 1925 檔 ≈ **5.3 年**。付 149 的人想看的是**自己手上的股票**,不是隨機 7 檔。 +**怎麼補**:**開放點播**——完整版每月可指定 1 檔(引擎已經能算任意檔,只是排隊順序問題)。這同時是最強的 basic→full 升級誘因,一石二鳥。 + +### 🟠 #4 旗艦也中空白頁 BLOCKER +full PDF **實體 24 頁,其中 12 頁是空白**(封面寫「共 12 頁」= 邏輯頁數正確)。與我在 SKU 驗證抓到的同一個根因(`.page` 用 A4 幾何但無 `@page` 規則 → Chromium 退回 Letter,每頁溢 50pt 把頁尾推到下一張)。fixer-sku 正在修 render_kit,**但要確認週報這條渲染路徑也吃到同一個修**。 +→ 付 149 打開發現每隔一頁就是空白,對「這東西專不專業」是致命一擊。 + +### 🟡 #5 12 處「代號 代號」名稱缺失 —— 像半成品 +PDF 實際印出「**2891 2891 +39,351**」「**3045 3045**」「**1102 1102 -62,173**」「**1432 1432**」…共 **12 檔**(1102/1423/1432/1446/2356/2387/2536/2891/3045/4137/5225/5706)。name map 對這些代號查不到名字就把代號印兩次。 +→ 中信金(2891)、台灣大(3045)、亞泥(1102) 都是大型股,散戶一眼就看得出「這報告連中信金的名字都不會寫」。**怎麼補**:name map 補上 twdata 的全市場代號→名稱對照(`get_universe` 已有),查不到時至少只印一次代號。 + +### 🟡 #6 S5 高殖利率榜零風險揭露 —— 對「怕被割的小白」反而危險 +Top15 幾乎全營建股,高殖利率多為建案認列的一次性配發。報告只列榜不警示,而頻道受眾定位正是新手。 +**怎麼補**:加一行「殖利率為歷史配發除以現價,建案型/景氣循環股的高殖利率常不可持續——這是位置陳述,不是推薦」。這與既有的「介紹≠推薦」紀律一致,成本極低。 + +--- + +## 5. 第一印象(打開前 2 頁像不像 149/月) + +- **封面**:暗金鎖色數據卡、字體層級、四個 KPI(溫度 45.4/覆蓋 1001 檔/板塊 34 類/訊號 19 已平倉)——**質感是有的,像付費品**。同款渲染我在 SKU 評審給過 M2 8 分,美術不是問題。 +- **第 2 頁**:**空白頁**。第一印象直接從「專業」掉到「半成品」。 +- 封面就把三個價格(99/149/1290)印在報告本體上 —— 已經付錢的訂戶每週看到價目表,體驗上是雜訊(那是 landing 的事,不是交付物的事)。 + +**判定:美術像 149,工程完成度像 beta。** 兩頁之內就露餡。 + +--- + +## 6. 總判定與最小補救 + +### 現在值不值 NT$149/月?——**不值。** +不是因為做得爛,是因為**可替代**:8 段裡 7 段的內容,台灣散戶用免費工具(看盤軟體/選股器)或更便宜的競品(CMoney ≈299 有即時籌碼)拿得到更好的版本。**唯一不可替代的是 S7**,而 S7 目前是隨機發牌。 +誠信(零造假、fail-closed、含輸單)是**真正的資產**,但它是**入場券不是賣點**——沒有人會因為「這份報告很誠實」而每月付 149,他們付錢是因為**這裡有別處拿不到的東西**。 + +### 要值得 149,最少要補什麼(依 CP 值排序) +1. **S7 開放點播**(完整版每月指定 1 檔)—— 把唯一護城河從樂透變成服務,同時製造 basic→full 升級動機。**若只能做一件事,做這個。** +2. **修空白頁 + 12 處名稱缺失** —— 這兩個不修,前面談的都是空話(產品看起來就不值錢)。 +3. **S8 每月真重跑,或拿掉** —— 消除「每週一樣」這個最確定的退訂觸發器。 +4. **S1 加「溫度 → 歷史動作對照」** —— 你有全市場歷史資料,做得出「溫度<30 時未來 20 日勝率 X%(樣本 N)」。這是唯一能把 basic 從「免費軟體都有」救起來的一刀,而且完全在誠信框架內(有樣本、有溯源、不喊單)。 +5. **S6 重新定位為信任錨 + 標樣本不足** —— 從勸退變差異化,零開發成本,只改文案。 +6. **S5 加殖利率陷阱警語** —— 一行字,消除對新手受眾的實害。 + +### 定價建議(補完之後) +- **basic 99 → 砍掉或降 49**,並放 1 檔 S7 當鉤子(現況 basic 沒有任何不可替代內容,99 賣不動)。 +- **full 149 維持** —— 補完 1~3 後,S7 點播 + 全市場 xlsx + 籌碼週流向,對照 CMoney 299 / M平方 533,149 是站得住的。 +- **年繳 1290 暫緩** —— 先跑 3 個月月繳、看真實 retention 再開;現在開年繳=把未驗證的產品鎖住不滿意的客戶。 +- **國際版 US$15 → 重訂 US$7–9**(Substack 對照組是誤引,真實分佈 $5–15,US$15 是天花板不是卡位價)。 + +--- + +> **給 Carson 的一句話**:這份週報的誠信做到了台灣財經內容幾乎沒人做到的程度(連自己訊號賠錢都印出來),**這是護城河的地基,但地基不是房子**。目前唯一蓋起來的房子是 S7——把它從「每週隨機發牌」變成「你想看哪檔就給你哪檔」,149 才有人買單。 diff --git a/docs/ecommerce/VERIFY_REPORT_c9538e6.md b/docs/ecommerce/VERIFY_REPORT_c9538e6.md new file mode 100644 index 0000000..00c460c --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_c9538e6.md @@ -0,0 +1,162 @@ +# VERIFY_REPORT_c9538e6 — 找碴驗證 team-lead 的修復 commit + +> 驗證人:verifier-sku2(fresh-context,未參與 c9538e6 實作)|日期:2026-07-16 +> 對象:`git show c9538e6`(修 phase3a 抓到的 B1 / A1 / A2 / A3) +> 立場:**試圖證明這個修法錯了或不夠**。重算部分**零 import 引擎**(自寫 Python 直讀 state.json / chips / valuation / csv / checkup facts);gate 鬆緊與 B1 邊界則必須 import 受測引擎本身(那正是待測物)。 +> 唯讀:未改任何程式碼/產物、未打外部網路、未動 .env(未讀任何密鑰值)。 +> 重現腳本:`scratchpad/verify_c9538e6.py`、`verify_c9538e6b.py`、`gate_stress.py`、`coverage_gap.py`、`b1_edge.py` + +## 總結 + +| 攻擊面 | 結論 | +|---|---| +| 存證是不是又一個空殼 | **PASS** — 64 筆獨立重算 **0 FAIL**;`source` 標籤**逐一可解析** | +| 覆蓋率是不是灌水 | **PASS** — **零**完全重複記錄;PDF 績效數字覆蓋率 **100%** | +| **gate 有沒有被弄鬆** | **PASS** — 8/8 段仍擋下憑空數字;`_bind` 相對舊 `_pool` 只多綁 1 個真來源值、**零漏綁** | +| A2 是不是真的沒截斷 | **PASS** — S7 71/71 逐字相同,0 筆斷在數字中間 | +| A3 tier-aware 反向漏洞 | **PASS** — 零洩漏,且 basic **沒被少記** | +| **B1 的 400 有沒有副作用** | **FAIL** — 正常流未受影響,但**「合法 JSON 但非物件」三平台仍 500** | + +**0 BLOCKER / 1 MAJOR / 2 MINOR。A1・A2・A3 三個修法我打不破——存證是真的,不是第二個空殼。B1 只修了一半。** + +> **併發雜訊聲明(兩處,皆已排除干擾)** +> 1. `pytest quant-service/ecommerce/tests` 現有 5 個 FAIL,全在 `test_product_factory_v2.py`,根因是 **fixer-sku 正在改的 `product_factory_v2.py:969` NameError**,該檔**不在 c9538e6 的改動清單內**(`git show c9538e6 --stat | grep -c product_factory_v2` → 0)。**c9538e6 自己的範圍:66 passed**(`test_weekly_report_v2.py` + `webhook/tests`),與 commit message 宣稱一致。 +> 2. `quant-service/webhook/app.py` 目前有**未提交改動,是 fixer-env 的**(加 `env_report_lines()` 啟動摘要,治我 runbook 報告的 B1 靜默失敗),非本驗證者所為。**關鍵確認:`git diff c9538e6 -- webhook/app.py | grep -c "_json_or_400"` → 0**,即受測的 `_json_or_400` 自 c9538e6 起未被任何人改動 → 下方 M1 的實彈結果**確實是對 c9538e6 版本的檢驗**,未被併發改動污染。 + +--- + +## MAJOR + +### M1 B1 只修一半:「合法 JSON 但不是物件」→ 三平台仍 500(retry storm 未除) + +`_json_or_400`(`app.py:29-40`)只捕 `JSONDecodeError` / `UnicodeDecodeError`。**合法 JSON 但不是 dict 時,它原樣回傳**,parser 立刻對它 `.get()` → `AttributeError` **未捕捉** → **500**。 + +實彈(in-process ASGI,假密鑰 + tmp 路徑,零外網;`scratchpad/b1_edge.py`): + +| 案例 | portaly | lemonsqueezy | whop | +|---|---|---|---| +| 壞 JSON `{not json`(B1 目標) | 400 ✓ | 400 ✓ | 400 ✓ | +| 空 body(B1 目標) | 400 ✓ | 400 ✓ | 400 ✓ | +| **合法 JSON 但是陣列** `[1,2,3]` | **500** | **500** | **500** | +| **合法 JSON 但是純字串** `"hello"` | **500** | **500** | **500** | +| **合法 JSON 但是數字** `123` | **500** | **500** | **500** | +| **合法 JSON 但是 null** `null` | **500** | **500** | **500** | +| 合法物件但欄位全缺 `{}` | 422 ✓ | 200 ✓ | 200 ✓ | +| 合法物件 `{"data":"oops"}` | 422 ✓ | **500** | **500** | +| 合法物件 `{"data":[1,2]}` | 422 ✓ | **500** | **500** | + +根因逐一確認: +``` +parse_portaly([1,2,3]) → AttributeError: 'list' object has no attribute 'get' +parse_lemonsqueezy("hello") → AttributeError: 'str' object has no attribute 'get' +parse_whop(None, 'wh') → AttributeError: 'NoneType' object has no attribute 'get' +``` +`parse_portaly` 之所以能擋掉 `{"data":"oops"}`,是因為它有 `isinstance(payload.get("data"), dict)` 這道 guard(`normalize.py:168`);LS / Whop 沒有。 + +**為什麼是 MAJOR 而不是 MINOR**:這**正是 B1 要根治的那個失效模式**——5xx 讓平台無限重投。commit message 寫「壞 JSON 一律 400 不是 500」「平台無限重投同一顆壞蛋」,但對整整一類 payload 沒兌現。`null` 特別現實(平台序列化 bug / 空事件送 `null` body 都會踩到)。觸發前提與原 B1 相同(需持有合法簽章 → 非無密鑰攻擊者可打,realistic 觸發=平台送邊界事件),故沿用原 B1 的嚴重度層級。 + +**修法建議(交給實作者,我未代修)**:`_json_or_400` 收尾加型別檢查—— +```python +obj = json.loads(body.decode("utf-8")) +if not isinstance(obj, dict): + raise HTTPException(400, "payload must be a JSON object") +return obj +``` +一行治全部三個平台,且與既有 fail-closed 風格一致。建議一併補回歸測試(退回修復要會紅)。 + +--- + +## MINOR + +- **N1 basic PDF 的免責區塊提及 basic 沒交付的段**:basic 版免責寫「S8 結構基準為 2026-06-12 靜態回測快照…估值段(**S5/S7**)只陳述數據位置」,但 basic 買家收不到 S5/S7/S8。屬**靜態 boilerplate**,是過度揭露而非短少(不影響誠信),但對 basic 買家略困惑。已確認**非內容洩漏**:basic PDF 的章節標頭只有 S1/S2/S3/S6,且 S7 專屬數字 `8728.8` 在 basic PDF **不存在**。 +- **N2 封面統計未經 `_bind` 綁定**:封面「強弱榜覆蓋 **1001** 檔」「板塊 **34** 類」是資料衍生的顯示數字,但沒進存證(非績效數字,gate 本就不管,不影響 fail-closed)。若要把「每個資料衍生的顯示數字都可回查」做滿,這兩個是殘餘。 + +--- + +## 各攻擊面證據 + +### ✅ 存證不是空殼:64 筆獨立重算,0 FAIL(涵蓋全 8 段) + +**零 import 引擎**,自己讀來源檔重算,逐筆對 `value`: + +| 段 | 來源 | 重算筆數 | 結果 | +|---|---|---|---| +| S1 | `state.json:gauge+index` | 11 | 11/11 命中(溫度 45.4 / breadth 40.9 / adr 1.8 / nh 95 / nl 88 / avg_rsi 48.8 / adv 1060 / dec 588 / flat 277 / index_chg 0.09 / index_price 106.4) | +| S2 | `state.json:sectors[*]` | 7 | 7/7(貿易百貨業 avg_chg 1.06 / bull_pct 63 / score 54 / count 19 / inst_count 15…) | +| S3 | `state.json:wave_top[*]` | 8 | 8/8(2634 chg 1.79・rsi 78.9・score 94・price 68.3;2910…) | +| S4 | `twdata/chips[*]` 本週日檔加總 | 6 | 6/6(2618 foreign_net_5d **223,296**;5880 44,465;2892 43,887…) | +| S5 | `valuation_20260716.json` | 10 | 10/10(pe_p25 14.24 / median 20.7 / p75 41.67 / sample_n 831;2442 殖利率 13.99・pe 3.68・pb 0.83…) | +| S6 | `state.json:track`(招牌) | 7 | 7/7(n_closed 19 / win_rate 15.8% / long 16.7% / short 15.4% / avg_r -0.61 / avg_ret -7.77% / n_open 278) | +| S7 | `stock_checkup_facts.json` | 71 | 71/71 逐字相同(見 A2) | +| S8 | `adaptive_per_stock.csv` | 14 | 14/14(sample_n 1770 / median 6.165 / n_pos 940 / pct 53.10734…;5386 a_net 10027.73…) | + +**`source` 不是亂標**:我用 `source` 字串裡的鍵(`sectors[貿易百貨業]`、`wave_top[2634]`、`chips[2618]`、`valuation:data[2442]`、`adaptive_per_stock.csv[5386]`)**回頭去來源檔查,全部找得到且數值相符**——沒有任何一筆 source 指向不存在的位置。 +S4 的視窗我獨立取「最後 5 個 chips 檔」= 2026-07-08/09/13/14/15,與存證 source 標的「本週日檔加總」一致。 + +### ✅ 覆蓋率不是灌水 + +- **完全重複的 `(section, field, value, source)` 組合 = 0 筆** → 沒有靠重複記同一數字撐筆數。 +- 283 筆有 `value`(commit 宣稱 283 ✓),261 個相異值;22 個重複是**跨欄位的合法碰撞**(如 9.0 同時是不同欄位的值),非灌水。 +- 每段筆數與該段實際內容量成比例,非「只記好記的」:S1 11 / S2 60(12 板塊 × 5 欄) / S3 120(30 檔 × 4 欄) / S4 32 / S5 49 / S6 7 / S7 71 / S8 14。 + +**A1 真正的衡量 —— PDF 有呈現但沒進存證的數字還剩多少:** + +| 版本 | PDF 相異**績效%宣稱** | 未進存證 | PDF 全部相異數字 | 未進存證 | +|---|---|---|---|---| +| full | 229 | **0(100% 覆蓋)** | 580 | 80 | +| basic | 59 | **0(100% 覆蓋)** | 230 | 39 | + +那 80 / 39 個「未覆蓋」我**逐一查了 PDF 語境**,結論:**零個是績效數字**—— +- **約 9 成是股票代號**(1101 台泥 / 1102 亞泥 / 1301 台塑 / 2330 台積電 / 5386 青雲 / 6505 台塑化 …) +- 其餘:定價 99 / 149 / 1290(來自 `config.py`,非資料來源)、年份 2026、頁碼、覆蓋檔數 1001、板塊數 34、`00878` 拆出的 878 +- **代號不入池是正確設計**(綁進去會把池撐大、反而弱化守門),故此殘餘不是缺陷。 + +> 誠實補充:我第一版抽取器把千分位逗號的 `+223,296` 切成 `223` 和 `296`,誤報 97 個未覆蓋。改成 comma-aware 後降到 80,且全為上述非績效 token。**我沒有把自己的抽取器 bug 當成對方的缺陷報出去。** + +### ✅ gate 沒有被弄鬆(最重要的一面) + +**攻擊**:對 8 段各自注入「憑空手打的績效數字」,看 fail-closed 擋不擋(手法對齊既有 `test_provenance_rejects_unsourced_number`): + +``` +S1 87.3% → ✓ 被擋 S2 87.3% → ✓ 被擋 S3 87.3% → ✓ 被擋 S4 87.3% → ✓ 被擋 +S5 87.3% → ✓ 被擋 S6 87.3% → ✓ 被擋 S7 45.7% → ✓ 被擋 S8 87.3% → ✓ 被擋 +結論:8/8 段擋下憑空數字,0/8 段放行 +``` + +**`_bind` vs 舊 `_pool` A/B 對照(S1,同一份真實資料)**: +``` +舊 _pool 池大小 = 11 / 新 _bind 池大小 = 12 +新增入池的值:[106.4] ← 唯一新增 +少掉的值:[] ← 零漏綁 +``` +唯一新增的 `106.4` = `state.json:index.price`,是**真實來源欄位**,且**本來就顯示在 PDF 上**(「0050 106.4 +0.09%」)。原本「顯示了卻沒綁」,c9538e6 把它綁上 → 這是**修正,不是放水**。 + +各段池仍然很小(S1=12 / S6=9 / S8=28),**鑑別力沒有被稀釋**——這正是 v1 刻意「空池起步」的設計意圖,`_bind` 完整保住了。S7=819 較大是其「逐字引用 + `_walk_numbers` 全走」的既有設計所致,非本次引入。 + +### ✅ A2 真的沒截斷 + +- S7 71 筆存證 `text` 與來源 `claim/summary` **逐字相同:71/71** +- text 長度 min=37 / **max=172** / 平均 87 → 遠超 v1 的 60 字上限,截斷確已移除 +- **0 筆**結尾斷在數字中間(v1 特徵如「卡瑪比率 0.」已消失) + +> 誠實補充:我的啟發式先報「5 筆長度剛好=60 → 仍在截斷」。逐筆查證後**推翻自己**:那 5 筆全是 `checkup_annual_extremes__*`(同一模板,天然就 60 字),與來源逐字相同。**這是我的誤報,不是缺陷。** + +### ✅ A3 tier-aware 兩個方向都對 + +- basic 存證 `sections_covered` = `['S1','S2','S3','S6']` == `CFG.SECTION_TIERS` 的 basic 段 ✓ +- basic 存證裡的 **full-only 記錄 = 0 筆**(零洩漏)✓ +- **反向(team-lead 特別要求查的漏洞)**:basic 各段筆數 **== full 同段筆數**(S1 11 / S2 60 / S3 120 / S6 7)→ **basic 該有的一筆都沒少** ✓ +- basic PDF 的 59 個績效%宣稱 **全部在 basic 存證檔內**(未覆蓋 0)✓ +- 交付面複驗:basic PDF **不含** S4 內容、S7 專屬數字 `8728.8` **不存在** ✓(S5/S7/S8 字樣只出現在靜態免責句 → 見 N1) + +### ✅ B1 正常事件流未被 400 誤傷 +`{}`(合法物件、欄位全缺)→ portaly **422**(缺 email 無法交付,既有設計)、LS / Whop **200**(事件名不在對應表 → IGNORED)。皆非 400 誤判,正常流未受影響。合法簽章 + 正常 payload 的四平台路徑在 `webhook/tests` 46 passed 中亦全綠。 + +--- + +## 值得肯定 + +1. **A1/A2/A3 我打不破**:64 筆獨立重算 0 FAIL、source 逐一可解析、零重複灌水、績效數字 100% 覆蓋、A2 逐字無損、A3 雙向皆對。**這次的存證是真的,不是 SKU 那批 `records=[]` 的同型空殼**——我帶著「你可能有同型瑕疵」的假設來查,查不到。 +2. **最該擔心的事沒發生**:為了讓存證好看而放鬆守門 —— **沒有**。8/8 段仍擋憑空數字,池只多了一個真來源值、零漏綁。 +3. **新增的 3 個測試釘得住**:`test_all_sections_write_provenance_records`(每段都要有 source+field)、`test_provenance_records_carry_structured_values`(存證數值須等同來源欄位值)、`test_s7_provenance_text_not_truncated`(逐字比對)——都是**會因退化而變紅**的真回歸,不是套套邏輯。 +4. commit message 的每個量化宣稱(71→364、283 有值、basic 198、零 full-only 洩漏、66 tests)**經查全部屬實**,無灌水。 diff --git a/docs/ecommerce/VERIFY_REPORT_gauge_history.md b/docs/ecommerce/VERIFY_REPORT_gauge_history.md new file mode 100644 index 0000000..2a00685 --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_gauge_history.md @@ -0,0 +1,173 @@ +# VERIFY_REPORT_gauge_history — 找碴驗證(動到產線掃描器) + +> 驗證人:verifier-sku2(fresh-context,未參與實作)|日期:2026-07-17 +> 對象:`quant-service/data_hunter/scan.py`(**正在跑的產線掃描器**,S1/S2/S3/S6 全靠它)+ `tests/test_gauge_history.py`(新增),未 commit +> 立場:**試圖證明它會弄壞掃描**。故障注入/突變/原子性模擬全部自己動手。 +> 唯讀:未改任何程式、**未跑全掃描**(不覆寫 state.json)、未打外部網路、所有寫入都在 tmp。 +> 重現:`scratchpad/attack_gauge.py`、`mutate_gauge.py` + +## 總結 + +| 攻擊面 | 結論 | +|---|---| +| 1 零行為改變 | **PASS** — hook 在 state.json 落地**之後**,且對 `state` 物件零副作用 | +| 2 絕不炸掃描 | **PASS** — 9/9 故障注入全部回 False 不拋;突變後容錯測試轉紅 | +| 3 冪等 | **PASS,但有一個 MAJOR 副作用**(見 M1) | +| 4 原子性 | **PASS** — 整檔重寫 **比 append-only 更安全**,team-lead 的擔憂不成立 | +| 5 足以重建 ④ | **PASS** — 我獨立重算 = 45.4,與官方逐位一致 | +| 6 既有測試 | **PASS** — data_hunter **335**、ecommerce **92**,與宣稱一致 | + +**0 BLOCKER / 1 MAJOR / 3 MINOR。** + +**掃描器的安全性我打不破——這部分做得很紮實。** 但它**靜默地達不到自己存在的目的**:看板 app 只要開著,每天最後一筆幾乎必然是 `confirmed=False`,半年後想濾出的「乾淨 confirmed 序列」會幾乎是空的。 + +--- + +## MAJOR + +### M1 看板 app 每 30 分鐘覆蓋掉當天的 confirmed 記錄 → 半年後這個檔等於白存 + +**這個改動的唯一理由**,是它自己 docstring 寫的:「盤中的讀數是**暫定值**、收盤後才是**確認值**……未來分析必須能濾出 confirmed 的那條乾淨序列」。 + +**但去重規則是「同日只留最後寫的那筆」,而最後寫的那筆幾乎必然是盤後的 realtime 掃描。** + +證據鏈: +1. `app.py:59` 背景迴圈無條件呼叫 `scan.run_once(push=True, realtime=True)` +2. `realtime=True` → `drop_last = realtime or intraday` = True → `confirmed_mode = not drop_last` = **False** +3. **盤中閘沒有守在 run_once 上**:`mh = _market_hours()` 只用來決定**睡多久**(`wait = 2 if mh else 30`),而且它自己會印「盤後」——這個迴圈是**設計成盤後繼續跑的** +4. → 看板開著時,**盤後每 30 分鐘就寫一筆 `confirmed=False`**,把當天 cron/`全市場掃描.bat` 寫的確認值蓋掉 + +實測重現(`attack_gauge.py`,模擬真實日程): +``` +14:00 cron(--full) → confirmed=True +app 盤後每 30 分跑 3 輪 → 檔案 1 筆,最終 confirmed=False mode=intraday +→ 當天的 confirmed 讀數被蓋掉了 +``` + +**為什麼是 MAJOR 而不是 MINOR**: +- 它**不會弄壞掃描**(安全面完全沒問題),但它讓這個改動**達不到唯一的目的**。 +- **失敗是靜默的**:沒有任何錯誤訊息,檔案看起來很正常、每天都有一筆。**要等到半年後真的要用時,才會發現 confirmed 幾乎全是 false** —— 而那時已經沒有時光機可以補。這正是這個檔存在的理由(資料無法回填),所以「晚半年才發現」的代價就是**再等半年**。 +- 這是本專案第二次出現「靜默失敗、要很久以後才會發現」的同型問題(前一次是 `.env` 載入斷鏈)。 + +**測試為什麼沒抓到**:`test_intraday_then_confirmed_keeps_confirmed` 只測了**有利的方向**(盤中→收盤,保留 confirmed ✓ 我實測也過),**沒測會壞的反方向**(收盤→盤中)。我實測反方向: +``` +收盤(confirmed=True) → 盤中(confirmed=False) → 最終 confirmed=False ⚠ 確認值被暫定值覆蓋 +``` + +**修法建議(約 3 行,我未代修)**:同日已有 confirmed 記錄時,不讓 unconfirmed 覆蓋 —— +```python +prev = next((o for o in rows if str(o.get("date",""))[:10] == day), None) +if prev and prev.get("confirmed") and not rec["confirmed"]: + return False # 已有確認值 → 盤中暫定值不覆蓋 +``` +並補一個反方向的回歸測試(`test_confirmed_is_not_clobbered_by_intraday`)。 + +--- + +## 各攻擊面證據 + +### ✅ 1 零行為改變(0 刪除 ≠ 沒副作用 —— 我另外查了) + +**hook 點位置正確**(`scan.py` run_once 尾段,逐行確認): +``` +1404 _atomic_write_json(STATE_FILE, state) ← 主產物先落地 +1405 if not state.get("ok"): return state ← ok 檢查 +1409 append_gauge_history(state) ← hook 在兩者之後 ✓ +``` +→ 就算 append 整個爆掉,`state.json` 早就寫完了。**宣稱屬實。** + +**對 `state` 物件零副作用(我實測)**:`deepcopy` 前後 `json.dumps(sort_keys=True)` 全等 → `True`。 +特別查了共享參照風險:`rec["components"] = g.get("components")` **持有 state 內部 dict 的參照**,但 `_json_safe`(`scan.py:132-141`)是**純函式**(用 comprehension 建新 dict/list,從不就地改),所以序列化不會回頭污染 `state`。`state.gauge.components` 呼叫前後完全相同。 + +### ✅ 2 絕不炸掃描(9/9 故障注入 + 突變) + +| 注入 | 結果 | +|---|---| +| `_atomic_write_text` 拋 `OSError(28, 磁碟滿)` | 回 False 未拋 ✓ | +| path 指向目錄(不可寫)→ PermissionError | 回 False 未拋 ✓ | +| `state=None` / `{}` / gauge 缺 / temperature=None / ok=False / date 格式壞 | 全部回 False 未拋 ✓ | +| gauge 含不可序列化物件 → `TypeError` | 回 False 未拋 ✓ | + +**突變測試**(用 AST 把函式最外層 try/except 拿掉再跑): +``` +test_write_failure_never_raises 🔴 轉紅 ✓ +test_unreadable_existing_file_never_raises 🔴 轉紅 ✓ +test_garbage_state_never_raises 🟢 仍綠 → 見 MINOR-2 +test_function_body_has_exception_guard (我的技術驗不了 —— 見下註) +``` +→ 防線**有被行為測試釘住**,不是只靠 source 檢查。 + +> 註:`test_function_body_has_exception_guard` 用 `inspect.getsource`,對我 AST 合成的模組會噴「source code not available」,那個紅是**我技術的假象**、不算它轉紅。它是 source 檢查測試,對真實檔案有效。**我沒有把這個假紅當成證據。** + +### ✅ 3 冪等 + 邊界(除 M1 外全過) + +| 情境 | 結果 | +|---|---| +| 同日跑兩次 | 1 筆 ✓ | +| 盤中(false)→ 收盤(true) | 1 筆,保留 **confirmed=True / mode=daily** ✓ | +| **收盤(true)→ 盤中(false)** | **confirmed 被覆蓋成 False** ⚠ **→ M1** | +| 跨日 3 天(亂序寫入) | 3 筆,且**依日期排序** `['07-14','07-15','07-16']` ✓ | +| 壞行混入(`{壞行`、空行) | 壞行丟棄、**好行保留**(07-10/07-11 留著)✓ | + +### ✅ 4 原子性 —— **整檔重寫比 append-only 更安全,不是更危險** + +**team-lead 的擔憂:「重寫途中被砍會不會丟全部歷史(唯一無法重建的資料)」→ 實測:不會。** + +`_atomic_write_text`(`scan.py:120-124`)= 寫 `.tmp` → `os.replace`。**`os.replace` 是原子的**:要嘛完全成功、要嘛原檔完全不動,不存在「寫到一半的主檔」。 + +我模擬「tmp 寫完、`os.replace` 前被砍」: +``` +既有 3 筆 → 注入 KeyboardInterrupt 攔在 os.replace → 被砍後檔案 = 3 筆,內容未變 True ✓ 原檔完好 +``` + +**兩種寫法的風險對照(我的裁決)**: + +| | 整檔重寫 + tmp/os.replace(現行) | append-only(`open(path,'a')`) | +|---|---|---| +| 寫入途中被砍 | **原檔完好**(replace 沒發生)✓ | **可能留半行**(torn write)→ 檔案本身損壞 | +| 已存在的壞行 | 讀取時丟棄、自我修復 ✓ | 永久留在檔案裡 | +| 同日重跑 | 天然冪等 ✓ | 會累積重複筆,要另外去重 | +| 效能 | 497B/筆 → 一年 121KB、**五年 606KB**;讀 1250 行+寫 0.6MB = 個位數毫秒,**非問題** | 略快(無意義的差距) | + +→ **現行寫法是對的**,而且比 append-only 更能保護這份不可重建的資料。**擔憂不成立。** + +殘餘風險見 MINOR-3(檔案若已損壞,壞行會被靜默丟棄 → 建議留 `.bak`)。 + +### ✅ 5 記錄足以重建 ④(這是整件事的目的) + +溫度公式(`scan.py:968-972`): +```python +temperature = (0.30*comp_rsi + 0.25*comp_breadth + 0.15*comp_adr + 0.20*comp_nhnl + 0.10*comp_vol) +if not mkt_long_ok: temperature *= 0.90 +temperature = round(temperature, 1) +``` +**我用記錄裡存下的欄位獨立重算**: +``` +components = {rsi 48.8, breadth 40.9, adr 64.3, nhnl 50.7, vol 8.0} +0.30*48.8 + 0.25*40.9 + 0.15*64.3 + 0.20*50.7 + 0.10*8.0 = 45.4500 +index_trend = "UP" → mkt_long_ok = True → 不乘 0.90 → round(45.45,1) = 45.4 +官方 temperature = 45.4 → ✓ 逐位一致 +``` +**關鍵:`mkt_long_ok` 可由記錄推導** —— `scan.py:822` `long_ok = (trend == "UP")`,而 `trend` 有存成 `index_trend`。**所以記錄是足夠的,不是白存。** + +### ✅ 6 既有測試 +`data_hunter/tests` **335 passed**(基線 320,+15)、`ecommerce/tests` **92 passed** —— 與宣稱一致,零回歸。 + +--- + +## MINOR + +- **MINOR-1 重建 ④ 的 `mkt_long_ok` 邊界**:`compute_index` 在 0050 資料不足(`len(df)<30`)時回 `(trend=None, long_ok=True)`。未來寫重建程式的人若照直覺寫 `long_ok = (trend=="UP")`,那些日子會**誤乘 0.90**(差 10%)。正確寫法是 `(trend=="UP") or (trend is None)`。**建議直接在記錄裡多存一個 `mkt_long_ok` 欄位**(它已經在 `run_once` 的作用域外了,要從 `build_state` 帶出來;或至少把這條規則寫進 docstring),免得半年後的人推錯——這個檔的價值就在半年後那次使用。 +- **MINOR-2 `test_garbage_state_never_raises` 名稱過度承諾**:它餵的五種爛輸入(`None`/`{}`/`{"ok":True}`/`gauge:None`/`date:None`)**全部在 early-return 就被擋掉**,根本走不到會拋例外的程式碼 → 拿掉 try/except 它**照樣綠**。它不是空測(有斷言真實行為),但**它驗的是 early-return、不是它名字說的 exception guard**。guard 本身由另外兩個測試釘住(都轉紅了),所以整體覆蓋沒有洞,只是這個測試名字會誤導後人。 +- **MINOR-3 沒有 `.bak`**:整檔重寫雖然原子,但**若檔案本身已經損壞**(例如硬碟壞軌讓多行變垃圾),下次重寫會把壞行**靜默丟棄**並持久化。極端情況(全部行壞掉)= 只剩今天一筆,**而這是唯一無法重建的資料**。建議 `os.replace` 前先把舊檔複製成 `.bak`(成本 ~100KB/次,微不足道),或在丟棄壞行數 > 0 時 print 警告。 +- **MINOR-4 產線目錄有未追蹤垃圾檔**:`data_hunter/` 下有 `1`、`5`、`22`、`30`、`dict`、`pd.DataFrame` 等疑似誤建檔案(非本次改動造成,pre-existing),與 `gauge_history.jsonl` 混在同一層。`.gitignore` 或清理一下,免得日後誤判哪個是產物。 + +--- + +## 值得肯定 + +1. **hook 點選得對**:放在 `state.json` 落地 + `ok` 檢查**之後**,並在 docstring 明講理由。這是「動產線」時最關鍵的一個決定,它做對了。 +2. **fail-safe 徹底**:9 種故障注入(含磁碟滿、不可寫、不可序列化)全部回 False 不拋,`print` 而不 raise —— 「寧可漏記一天,也不能讓掃描器掛掉」不是口號,是實際擋得住。 +3. **原子寫入沿用既有 `_atomic_write_text`**,不自造輪子;壞行丟棄的自我修復設計比 append-only 更能保護這份資料。 +4. **`components` 存得夠**:我獨立重算逐位命中 45.4 —— 目的達成(除了 M1 那個誰是「最後一筆」的問題)。 +5. **docstring 把「為什麼值得動產線」寫清楚**(沒有時光機、今天不記半年後還是做不出來),並引用了 ④ 的裁決結論。這是負責任的產線改動該有的樣子。 diff --git a/docs/ecommerce/VERIFY_REPORT_phase3a.md b/docs/ecommerce/VERIFY_REPORT_phase3a.md new file mode 100644 index 0000000..826f347 --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_phase3a.md @@ -0,0 +1,64 @@ +# VERIFY_REPORT_phase3a — 先行驗證(找碴式) + +> 驗證人:funnel-face(未參與 A/B 兩元件實作,fresh-context)|日期:2026-07-16 +> 立場:**試圖證明它們做錯**。方法:獨立 Python 重算(不 import 週報引擎)+ webhook 實彈(in-process ASGI HTTP,假密鑰+tmp STUDIO,零外網、不碰真 .env/正式檔)。 +> 重現腳本:`scratchpad/recompute_weekly.py`(A)、`scratchpad/webhook_fire.py`(B)。 + +## 總結 +| 區塊 | 檢查數 | PASS | FAIL/發現 | 紅線結論 | +|---|---|---|---|---| +| A 週報誠信溯源 | 33 重算 + 71 S7 逐字 | 33/33 重算全對、71/71 數字⊆來源 | 3 個發現(皆非造假) | **無造假,產品數字全部忠於來源** | +| B 金流 webhook 實彈 | 34 | 32 行為正確(1 為我測試斷言誤判) | 1 個真發現(B1) | **簽章/去重/退款/名冊全對,1 個 500 robustness 缺口** | + +**最重的問題排序**:B1(MEDIUM,500 裸奔會誘發平台 retry storm)> A1(MEDIUM,溯源檔只覆蓋 S7)> A2(LOW-MED,溯源紀錄被截斷)> A3(LOW)。**沒有任何紅線級造假/漏帳/偽造放行**。 + +--- + +## A. 旗艦週報 v2 — 誠信溯源重算 + +### A-PASS(獨立重算,容差 abs 0.25 / rel 0.5%,全部命中) +- `[PASS]` S1 溫度 45.4 / 站上20MA 40.9% / ADR 1.8 / avgRSI 48.8 / 新高95 新低88 / 漲1060 跌588 平277 / 0050 106.4 +0.09% — 直讀 state.json.gauge+index,逐欄一致。 +- `[PASS]` S2 Top1 板塊(依 score 降序)貿易百貨業 score54.0 均漲1.06% 多方63% count19 inst15;Top5 score [54.0,53.6,51.9,51.1,49.9] 排序正確。 +- `[PASS]` S3 全市場強弱榜 Top1(wave_top 依 score 降序)2634 score94.0 chg1.79 rsi78.9;Top5 [2634,2910,3532,4541,6505] 一致。 +- `[PASS]` S4 法人5日加總:視窗=**最後5個 chips 檔 2026-07-08,09,13,14,15**(標示 07-08~07-15);台塑化2618 foreign_net 5日=+223,296、台泥1101=-45,307,逐檔重算命中;賣超 Top3 [1314,1303,1102] 一致。 +- `[PASS]` S5 估值分布:非空 PE(**831 檔,PE>0**)P25=14.25 / 中位=20.70 / P75=41.66,五種 percentile method 全在容差內(呈現 14.2/20.7/41.7);殖利率 Top5 [2442 13.99%…] 一致。 +- `[PASS]` S8 結構基準:**全樣本 1770 檔未過濾**,a_net 中位=6.165(呈現 6.2)、正報酬佔比=53.107%(呈現 53.1%);Top5 by a_net [5386,2383,2028,6223,2486] 一致。 +- `[PASS]` S6 誠實成績單(招牌,重點攻擊面):n_closed=19、win_rate 0.158→15.8%、avg_r -0.61、avg_ret -7.77%、long 16.7%、short 15.4%、n_open 278 — 全部與 state.json.track 逐欄一致。 +- `[PASS]` S7 逐字:71 筆溯源紀錄的數字**全部⊆ 來源 claim 字串**(2327 國巨 +21051.3% 這種 >10000% 怪物數也命中,靠 _pool_fg tokenizer);PDF/HTML 渲染的是**完整**來源 claim。 +- `[PASS]` Tier 分層(產品面):basic 只渲染 S1/S2/S3/S6、full 渲染全 8 段;**full-only 深度內容(S4/S5/S7/S8)不會外洩給 basic 買家**(basic.html 章節標頭僅 S1,S2,S3,S6;2330 8728.8% 等 S7 內容 basic 無)。 + +### A-發現(皆非造假,屬溯源「檔案」品質缺口) +- `[FAIL:MEDIUM]` **A1 溯源檔只覆蓋 S7**:`weekly_..._provenance.json` 71 筆**全是 S7**;S1–S6、S8 呈現的全市場數字(溫度、榜單、5日籌碼、percentile、中位數、真實戰績)在溯源檔裡**零紀錄**。fail-closed gate 仍對全段跑(數字不在 in-memory pool → 降級,故憑空造假擋得下),但**持久化的稽核檔只覆蓋 1/8 段** → 規格「每個綁定數字→來源存證」只對 S7 兌現,外部稽核者無法只憑該檔驗 7/8 段。 + - 重現:`python -c "import json,collections;p=json.load(open('quant-service/output/ecommerce_ready/weekly_v2/weekly_2026-07-16_full_provenance.json',encoding='utf-8'));print(collections.Counter(r['section'] for r in p['records']))"` → `Counter({'S7': 71})`。 +- `[FAIL:LOW-MED]` **A2 溯源紀錄被截斷**:`weekly_report_v2.py:564` 存 `"text": txt[:60]`,**51/71 筆被切斷**,部分斷在數字中間(如 `卡瑪比率 0.`、`…9.0 年(3`)。**產品 PDF 渲染的是完整忠實文字**(3292/0.99 都在),只有稽核用 JSON 的尾巴壞掉 → 稽核檔對長 claim 具誤導性(看起來像壞掉的數字)。 + - 重現:上面同檔取 `field=checkup_long_horizon__00878` 的 `text` → 尾為 `卡瑪比率 0.`;來源 claim 尾為 `卡瑪比率 0.99)`。 +- `[FAIL:LOW]` **A3 value 全 null + 溯源檔非 tier-aware**:71 筆 `value` 全 `null`(從不存結構化數值,溯源靠文字/pool 比對而非「數值綁欄位」);且 basic 與 full 的溯源檔內容相同(都含 71 筆 S7),basic 實際不渲染 S7 → 稽核檔描述了未交付給 basic 的段落。 + +> **可疑但已查證忠實(即使 PASS,列出供覆核)** +> 1. **S6 win_rate 15.8%(招牌)**:忠實引用 state.json.track,但該檔 `recent[]` 278 筆**全是 open**,19 筆已平倉的逐筆明細不在檔內 → 這個誠信招牌數字**無法從 state.json 自身重新導出**,可信度完全繫於 data_hunter 的 track 聚合(週報引擎無責,但這是最該盯的單點)。 +> 2. **S4 五日視窗排除了 07-07**:chips 目錄有 07-07 檔卻被 last-5 切掉(取 07-08~07-15)。標示「本週5交易日」誠實,但讀者可能預期含 07-07;屬邊界標籤細節。 +> 3. **2327 國巨 +21051.3%**:>10000% 靠 `_pool_fg` 特例入池才過 gate,視覺驚人;已驗證數字確實來自來源 claim,忠實無誤。 + +--- + +## B. 金流 webhook v2 — 實彈 + +環境:`TestClient(build_app(Settings(tmp路徑, 假密鑰, 假 adder/sender/ntfy, dry_run=False)))`,in-process ASGI 真 HTTP 往返,background task 有跑,零外網。 + +### B-PASS(32 項行為正確) +- `[PASS]` 合法簽章 → 200:Gumroad(token via query)/Portaly(HMAC)/Whop(Standard Webhooks)/LemonSqueezy(HMAC hex)四平台全 accepted。 +- `[PASS]` 真寫入:sales_ledger 記帳、customers_book 顧客簿、subscribers_book 名冊(Portaly 訂閱 status=active、tier 依 149 TWD 正確歸 `full`)、交付信送出(fake sender 收到 4 封)、revenue 分幣別(product/TWD 990)。 +- `[PASS]` 偽造簽章 → 401:四平台全擋(Portaly/LS 錯 HMAC、Gumroad 錯 token、Whop 錯 v1 sig);**偽造事件未寫入**帳簿。 +- `[PASS]` 缺密鑰 → 503:清掉 env 重建 app,Portaly/Gumroad/LS 全回 503(拒絕「無法驗證來源」)。 +- `[PASS]` 重放去重(冪等):同一 sale_id ×3 → **ledger 僅 1 筆、revenue.record 僅呼叫 1 次**(重放在 `append_sale` 回 False 時提前 return,不進記帳/交付)。 +- `[PASS]` 退款:Portaly refund → **ledger 負值沖銷 -149、revenue 負值 -149、名冊 status=cancelled**;退款重放 → 僅 1 筆沖銷(`:refund` 專屬去重鍵)。 +- `[PASS]` 攻擊面:Gumroad token 走 query 與 X-Ping-Token header 皆可;hex 簽章大寫+前後空白仍過(hex 大小寫不敏感、strip,正確);Gumroad token 尾空白 strip 後過、**大小寫錯誤被擋**(token 精確比對)。 +- `[PASS]` 超大 payload(200KB)→ 200 正常處理,未 500;整批畸形轟炸後 `/health` 仍 200,**服務進程存活**。 + +### B-發現 +- `[FAIL:MEDIUM]` **B1 畸形但簽章合法的 JSON → HTTP 500 裸奔**:`app.py:68/78/86`(LemonSqueezy/Whop/Portaly)在驗簽通過後 `json.loads(body)` 無 try 包覆;傳空 body 或壞 JSON 且**簽章合法**時 → `JSONDecodeError` 未捕捉 → 回 **500**(非 4xx)。Gumroad 路徑免疫(`parse_qs` 不會炸)。 + - 觸發前提:需**持有 webhook 密鑰**才簽得出合法簽章 → 非無密鑰攻擊者可打(realistic 觸發=平台送出截斷/空 body 邊界事件)。但 5xx 會讓 webhook 平台**持續重試(retry storm)**,而 4xx 不會 → 生產上一個畸形投遞可能變重試風暴。服務進程不死(health 仍 200),屬單請求未處理例外。 + - 重現:`scratchpad/webhook_fire.py` §6 —— 用 `PORTALY_SECRET` 對 `b"{not json"` 簽名 POST /sale-ping/portaly → 500;對 `b""` 簽名 POST /sale-ping/lemonsqueezy → 500。 + - 建議修:三處 `json.loads` 包 try → `raise HTTPException(400, "malformed JSON")`(對齊既有 fail-closed 風格),讓平台收 4xx 不重試。 + +> 註:B 測試以 TestClient(in-process 真 ASGI HTTP)代替 uvicorn 綁 port——完整跑過路由→驗簽→正規化→業務→原子寫檔全鏈,且可證明零外網、未碰真 .env/正式 STUDIO(全指 tmp)。§4「replay revenue recorded once」在腳本首跑顯示 FAIL,經查為我斷言把 note 截在 [:30] 藏掉 order id 所致;獨立複跑(3 次重放 → revenue.record 僅 1 次)證實冪等正確,已於報告計為 PASS。 diff --git a/docs/ecommerce/VERIFY_REPORT_phase3b.md b/docs/ecommerce/VERIFY_REPORT_phase3b.md new file mode 100644 index 0000000..d1b6e8e --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_phase3b.md @@ -0,0 +1,216 @@ +# VERIFY_REPORT_phase3b — 一次性 SKU 找碴驗證 + 掏錢評審 + +> 驗證人:verifier-sku(未參與 SKU 實作,fresh-context)|日期:2026-07-16 +> 立場:**試圖證明這 8 個 SKU 做錯了/不值得買**。方法:自寫獨立 Python 直讀來源檔重算(**零 import `product_factory_v2`**)+ PDF/xlsx 逐格解析 + scratchpad 內重現渲染證因果。 +> 唯讀:未改任何產物/代碼、未打外部網路、未動 `.env`。重現腳本:`scratchpad/verify_a.py`、`scratchpad/recompute_sku.py`。 +> 容差:絕對 0.25 / 相對 0.5%。 + +## 總結 + +| 面 | 結論 | +|---|---| +| **A 誠信溯源** | **數字本身零造假**——獨立重算約 **8,940 個數字/格,0 不符**。但**稽核檔 8/8 全空殼**、**成品有 gate 看不到的硬編碼數字且其中 1 個現在就是不實宣稱**。 | +| **B 掏錢品質** | **全部 8 支 PDF 每張內容頁後面都跟一張空白頁**(C2 @NT$1280 = 18頁裡 9 頁空白)。英文版 C2_en **37.5% 內容是中文**。 | + +**判定:不能上架。** 4 個 BLOCKER,其中 2 個是「對外不實/動錢」紅線級(C2 封面 11 項不實、英文版半殘)。 +**但要說清楚:沒有任何編造統計數字。** 8,940 格重算零誤差,產線的「數字忠於來源」這件事是真的做到了 —— 壞的是**稽核存證、成品排版、英文在地化、與少數硬編碼文案**。 + +--- + +## A 面:誠信溯源重算(7 項) + +### `[PASS]` A1 溯源重算(要求 ≥24 個數字,實測 ≈8,940) + +**因為 8/8 `_provenance.json` 的 `records` 全空(見 A3),無法「從 provenance 抽數字」——改用更嚴的做法:直接從成品(PDF/HTML/xlsx)抽呈現值,自己讀來源檔重算。** + +| 對象 | 重算量 | 結果 | +|---|---|---| +| M2 台積電體檢(2330) | 16 個數字 | 16/16 命中 | +| T2 鴻海體檢(2317) | 16 個數字 | 16/16 命中 | +| C1 全市場回測 xlsx | 1770 列 × 5 欄 = **8,850 格** | **0 不符** | +| C2 權值股總覽 xlsx | 41 格 | 0 不符 | +| C1 摘要 PDF 統計 | 6 項 + Top20 | 全中 | +| M1 當沖清單 | 21 碼 + 3 計數 | 逐碼相同(含前導零) | + +證據(逐欄 diff,全在容差內): +- 2330:年化 25.1%←`cagr` 0.2511857×100=25.1186 / 最大回撤 -46.5%←-0.4652652×100 / 20年總報酬 8728.8%←`total_return` 87.2881×100 / All-in +1746.9%←17.4693536×100 / 定投 +669.2%←6.6920656×100 / 0050 +751.5%←7.5154455×100 / 本益比 32.5、P25 15.6、中位 19.3、P75 25.9、第98百分位←`valuation_position` 逐欄 / 年營收 38,091億←3.80905e12÷1e8 / EPS 66.3←66.26 / 毛利率 66.2%←66.2 / 最長套牢 10.7年。 +- 2317:年化 10.4%←0.1041622×100 / 回撤 -75.0%←-0.7500586×100 / 總報酬 625.1%←6.2513954×100 / All-in +284.9%、定投 +176.3%,全命中。 +- C1:`twdata/adaptive_per_stock.csv` 1770 列 = 成品 1770 列,`a_net/a_pf/a_dd/a_tr/a_win` 逐格重算 **8,850 格零誤差**;排序確為 `a_net` 降序(10027.73→…→-97.7)。 +- C1 摘要:正報酬 940/1770(重算 940)、53.1%(重算 53.1073%)、中位 6.2%(重算 6.1650)、上市 971/上櫃 799(重算相同)。 +- M1:21 碼與 `daytrade_eligibility_20260713.json` 的 `disposition` **逐碼相同、差集為空**,含 `059570`/`066762`/`911608` 前導零完整;注意股 0←`attention: []`。 +- 誠實加分:`00878` 無估值資料 → C2 xlsx 該格**留空,沒有捏造**。 + +> 重現:`python scratchpad/verify_a.py` + +### `[PASS]` A2 單位/縮放攻擊(實作者踩過的 ×100 雷) + +逐欄判量級 + 回源驗單位,**沒有找到第二顆同型地雷**: + +- **`latest_dividend_yield`(高風險點)**:成品 2330 顯示 0.9%,來源 0.91。**不靠代碼註解,用跨股量級反證**:2603 陽明=8.23、2412 中華電=3.91、2882 國泰金=3.64、2317 鴻海=3.04、2408 南亞科=0.3。若此欄是比率,陽明殖利率將是 **823%** → 荒謬。故該欄確為百分比,「勿再 ×100」的處理**正確**。全 7 檔 0.3%–8.23% 皆合理。 +- **ratio→% 該乘的有乘**:`cagr`/`max_drawdown`/`total_return`/`three_way.*` 全為比率,成品一律 ×100,重算逐一命中(見 A1)。 +- **本益比**:8.5(2603)–58.3(2454),無 <1 的荒謬值 ✓ +- **金額量級**:年營收 3.80905e12 NTD → 38,091 億(÷1e8 正確)✓;EPS 66.26 元合理 ✓ +- **回測極端值**:+10027.7%(青雲5386)/ -97.7%(點晶3288)雖驚人,但**與來源 CSV 逐格相同**,非放大 100 倍所致 ✓ + +### `[FAIL:BLOCKER]` A3 未進 provenance 的數字 / fail-closed 破口 + +**三個獨立問題,由重到輕:** + +**(a) 8/8 `_provenance.json` 的 `records` 全空 —— 直接違反規格。** +``` +gumroad/C1_en pool_size=49 len(records)=0 magnet/M1_zh pool_size=32 len(records)=0 +gumroad/C2_en pool_size=328 len(records)=0 magnet/M2_zh pool_size=65 len(records)=0 +portaly/C1_zh pool_size=49 len(records)=0 shopee/T1_zh pool_size=21 len(records)=0 +portaly/C2_zh pool_size=328 len(records)=0 shopee/T2_zh pool_size=70 len(records)=0 +``` +根因:`Provenance.records` 的**唯一寫入點是 `Provenance.num()`**(`product_factory.py:126`)。 +**`product_factory_v2.py` 呼叫 `prov.num()` 的次數 = 0**(v1 = 15 次);v2 全改走 `disp()`/`_pool()`/`_pool_src()`,這三個**只寫 `prov.pool`、從不寫 `records`**。於是 `write_sku()`(`product_factory_v2.py:888`)存出的 `records` 恆為 `[]`。 +規格明文要求:`REDESIGN_SPEC_product.md:263`「`product_factory.Provenance.num()`:數字仍綁來源欄位、寫 `_provenance.json`」,且同檔 line 20 列 v1 溯源為「**baseline 誠信面(要保留、不可退化)**」→ 這是**規格違反 + 相對 v1 的退化**。 +- 重現:`grep -c "prov.num(" quant-service/ecommerce/product_factory_v2.py` → `0` +- **公允說明**:fail-closed gate 本身**確實有接上且會擋**(`main()` line 933-939 見 `bad` 即 `continue` 不出檔),且我獨立重算 8,940 格證明**數字全部忠於來源**。所以這是**稽核可追溯性的空殼化,不是造假**。但外部稽核者拿到 `_provenance.json` 得到零資訊 —— 對「即將真的賣錢」的商品,存證檔是誠信的對外證明,不能是空的。 + +**(b) 硬編碼數字繞過 gate(結構性盲區)。** +`FG.extract_claims()` 只抽「**績效類百分比宣稱**」(`fact_source_guard.py:188`,非績效語境不抽)。因此成品封面的 `11`(`product_factory_v2.py:386 / 486 / 627`)是**寫死的字面量**,既不經 `disp()` 綁來源,gate 也**結構上看不到**。→ 見 (c),它現在就在說謊。 + +**(c) 「每檔維度 11 項」對 C2 的 3/9 檔不實(現行成品,非潛在)。** +C2 合輯封面(`:627`)硬寫「覆蓋個股 9 檔 **每檔維度 11 項**」,實查來源: + +| 代號 | 名稱 | 實際維度 | | +|---|---|---|---| +| 2330/2317/2454/2603/2412/2408 | | 11 | ✓ | +| 2882 | 國泰金 | **10** | ✗ | +| 00878 | 國泰永續高股息 | **7** | ✗ | +| 2327 | 國巨* | **6** | ✗ | + +→ **NT$1280 / US$39 的最高價 SKU,封面宣稱對 3/9 檔(33%)不實。** 這是對外不實宣稱,踩紅線。 +- 重現:`{k.replace('checkup_','').split('__'+code)[0] for k in results if k.endswith('__'+code) or f'__{code}__' in k}` 逐檔取集合大小。 + +**(d) 同型地雷延伸到 T2(潛在)。** `build_T2`(`:469-475`)用 `codes = [c for c in by_code if c != "2330" ...]; code = codes[0]` **動態挑股**,封面卻硬寫 11 項。今天 `codes[0]`=2317(11 項,**碰巧為真**);`by_code` 順序隨體檢引擎每日重生而變,一旦輪到 2882/00878/2327,**付費 SKU 就會出貨不實宣稱且 gate 攔不到**。 + +### `[FAIL:MAJOR]` A4 listing 文案 + +- `[PASS]` **誇大詞:8/8 全乾淨。** 掃 18 個字(穩賺/保證/翻倍/必賺/無風險/躺賺/明牌/飆股/財富自由…)→ **零命中**。 +- `[PASS]` **中文版免責/介紹≠推薦:6/6 齊全**(M1/M2/T1/T2/C1_zh/C2_zh 皆含「非投資建議」或「介紹不等於推薦」)。 +- `[FAIL]` **英文版免責缺失**:`C2_en` description **完全無任何免責字樣**(掃 not investment advice / educational / risk / disclaimer → 零命中);`C1_en` 僅有一句 "Historical stats only."。**兩者的 `seo_note` 卻都自稱「含免責」** → 存證欄位本身不實。中文版有、英文版掉,對即將收 US$35/39 的國際買家是合規缺口。 +- `[FAIL]` **M1 數字/新鮮度宣稱與成品不符**:listing 標題「(**每日更新**)」、描述「附**今日快照**」,但成品內容是 `daytrade_eligibility_20260713.json`,**generated_at=2026-07-16 → 落後 3 天**,來源缺 0714/0715/0716 三個交易日(抓取器似乎自 07-13 起就沒再跑)。成品封面誠實標了「快照日 2026-07-13」**(這點值得肯定)**,但**listing 說的「今日」是假的**,且此 SKU 的賣點正是「報單前 30 秒防呆」——處置股每日變動,3 天前的清單拿去當沖防呆**有實害**。 +- `[FAIL]` **「11 項」**:見 A3(c)。 +- `[FAIL:MINOR]` **T2 交付與描述不符**:listing 寫「**任一**覆蓋權值股的 11 項完整體檢」,暗示買家可指定;實際 `build_T2` 固定出貨 `codes[0]`(今為鴻海),買家**不能選**。 + +### `[PASS]` A5 xlsx 代號前導零 + +`00878` 在 C2_zh / C2_en / T1「真實對照」三處**皆為 `str` 型 `'00878'`,前導零完整保留**;全 5 個 xlsx **無任何代號被轉成數字**(numeric=0)。M1 的 `059570`/`066762` 於 PDF 亦完整。✓ + +### `[FAIL:MAJOR]` A6 xlsx 公式(T1 定投模板) + +**T1 出貨的模板,買家一開檔就會看到荒謬的平均成本 92.5。** + +第 3 列 `D3=10`(買進股數)但 **`E3` 成交價空白**,公式照跑: +``` +F3 = D3*E3 = 10 × (空白→0) = 0 +G3 = G2+D3 = 10+10 = 20 +H3 = H2+F3 = 1850+0 = 1850 +I3 = H3/G3 = 1850/20 = 92.5 ← 買家看到「平均成本 92.5」(0050 買在 185) +``` +第 2 列正確(平均成本=185.0)。→ NT$99 商品的**核心賣點就是「自動算平均成本」,而出貨檔第一眼就是錯的**。 + +**加乘問題:只有第 2、3 列有公式。** 第 4 列起全空(無公式、無資料驗證、無 autofilter),但 listing/導引宣稱「**每月買進登一列,平均成本自動算**」——買家從第 4 列開始登,**什麼都不會自動算**,得自己複製公式。 +- 重現:`openpyxl.load_workbook(...)['定投追蹤']`,列出含 `=` 開頭的列 → 只有 `[2, 3]`。 + +### `[FAIL:MAJOR]` A7 xlsx 條件格式方向(C2 回撤色階反向) + +`2FB877`=綠、`E5484D`=紅、台股慣例紅=漲/好、綠=跌/壞。 + +| | 欄位 | 值符號 | colorScale | 效果 | +|---|---|---|---|---| +| **C1**「最大回撤%」F 欄 | 50.26, 47.3…(**正**=幅度) | min→紅, max→綠 | 回撤最小(好)=紅、最大(壞)=綠 | **✓ 正確** | +| **C2**「最大回撤%」D 欄 | -46.5, -75, **-98.5**…(**負**) | min→紅, max→綠(**同一組設定**) | **最深 -98.5%(最糟)=紅**、最淺 -22.3%(最好)=綠 | **✗ 反向** | + +兩張表對**同一個指標用了相反的符號約定,卻套同一組色階方向** → C2 翻轉。 +關鍵佐證:C2 xlsx 第 11 列自己的圖例寫「**色階:高年化=紅**、回撤反向(台股慣例)」——即該表**自定義紅=好**;但同表 D 欄卻把最糟的 -98.5% 塗紅。**同一張表裡,紅在 C 欄代表「最好」、在 D 欄代表「最糟」**,買家掃風險時會**讀反**。 +(公允:若採「紅=危險警示」的另一種讀法,D 欄自身說得通 —— 但那就與同表 C 欄及其自身圖例「高年化=紅」直接矛盾,兩種讀法下都是缺陷。) + +**A 面統計:3 PASS / 4 FAIL**(A1 A2 A5 PASS;A3 A4 A6 A7 FAIL) + +--- + +## B 面:「值得掏錢嗎」逐 SKU 評分 + +### 先講一個打穿全部 8 支的問題(BLOCKER) + +**每一張內容頁後面都跟一張空白頁。** 8/8 PDF 全中: + +| SKU | 實體頁 | 空白頁 | 定價 | +|---|---|---|---| +| C2_zh 合輯 / C2_en | 18 | **9** | NT$1280 / US$39 | +| M1 當沖 / T1 導引 | 6 | 3 | 免費 / NT$99 | +| M2 / T2 / C1_zh / C1_en | 4 | 2 | 免費 / 149 / 990 / $35 | + +空白頁只有頁首一條頁尾殘影(36–41 字元),其餘整片空白(已渲染成圖確認)。 + +**根因(已在 scratchpad 重現證明因果)**:`.page{width:210mm;height:297mm;page-break-after:always}` 是 **A4** 幾何,但產出的 HTML **完全沒有 `@page` 規則** → `pg.pdf(prefer_css_page_size=True)`(`render_kit.py:232`)無 CSS 頁面尺寸可「prefer」→ Chromium **退回預設紙張 US Letter(612×792pt)**。每頁 297mm(≈842pt)灌進 792pt 的紙 → **每頁溢出約 50pt**,`page-break-after:always` 再把那條溢出(含頁尾列)推到**下一張紙**。 +`render_kit.py:219` docstring 寫「HTML → **A4** 深色 PDF」,實際出的是 Letter —— 註解與行為不符。 + +**實測對照(scratchpad,未動產物)**: +``` +現行參數(prefer_css_page_size=True,無 @page): 頁數=4 紙張=612x792(Letter) 近空白頁=2 +改 format="A4" : 頁數=2 紙張=596x843(A4) 近空白頁=0 +``` +→ 一行修正即可全解;此 bug 同時解釋了「封面 · 共 2 頁 / 頁尾 2 / 2」與實體 4 頁的矛盾(邏輯頁數是對的,實體紙張多一倍)。 + +### 逐 SKU 評分(付費者視角) + +| SKU | 定價 | 分數 | 一句話 | +|---|---|---|---| +| **M2** 台積電體檢(magnet) | 免費 | **8/10** | 真的好——11 維度、暗色數據卡專業、「連你要熬幾年套牢都算給你看」的角度誠實又戳痛點,**值得留 email**;扣分只因 4 頁有 2 頁空白。 | +| **C1** 全市場回測數據包 | NT$990 | **6/10** | 資料本身是真資產(8,850 格零誤差、可排序篩選凍結)且摘要主動講「看中位數」很誠實;**缺**:「自適應」全文出現 6 次卻**從未定義**,無參數/週期/**手續費/滑價**/倖存者偏誤揭露,xlsx **無欄位說明表、0 個註解** → NT$990 買到一組**無法複現、不知是否含成本**的黑箱數字。 | +| **M1** 當沖適格清單(magnet) | 免費 | **5/10** | **缺**:資料落後 3 天卻宣稱「今日/每日更新」(此 SKU 賣點是盤前防呆,過期=有實害);內容本質是 TWSE 公開清單原樣重貼,無附加分析;6 頁 3 頁空白。防呆 checklist 是唯一加值。 | +| **T2** 鴻海體檢單檔 | NT$149 | **5/10** | 內容紮實(與 M2 同級),但**與免費的 M2 完全同模、只換一檔股票**——拿過免費 M2 的人很難再掏 149;listing 說「任一覆蓋權值股」實際**不能選**(固定鴻海);4 頁 2 頁空白。 | +| **C2** 權值股體檢合輯 | NT$1280 | **5/10** | 單檔質感高、9 檔一次擁有有價值,但**最高價 SKU 卻問題最多**:18 頁**9 頁空白**、封面「每檔維度 11 項」**對 3/9 檔不實**、回撤色階**反向**、且 00878(7維)/2327(6維)資料明顯較薄卻與 11 維檔同價未揭露。 | +| **T1** 定投追蹤模板 | NT$99 | **4/10** | **缺**:模板本體=**一條 `=H/G`**,且出貨檔第 3 列直接顯示**平均成本 92.5**(荒謬值);第 4 列起**不會自動算**,與「每月登一列自動算」的賣點不符;無資料驗證/無圖表/無報酬率欄位。真正有價值的是「真實對照」那張表(8 檔實算),但那是 C2/M2 已有的料。 | +| **C1_en** Full-Market Pack | US$35 | **3/10** | **主交付物 xlsx 完全沒在地化**:分頁名 `全市場回測`/`風險明細`、表頭 `代號/名稱/市場/自適應淨報酬%/獲利因子…` **全中文** → 英文買家**看不懂自己買了什麼**;PDF 9.1% 是中文(免責標題「免責與資料來源」、整段中文來源註、頁碼「封面 · 共 2 頁」、頁尾「介紹 ≠ 推薦」);Top20 個股名全中文無羅馬拼音;英文本體文案本身其實通順(非機翻感),但 "a few winners don't make a strategy universal" 小寫開頭是斷句瑕疵。 | +| **C2_en** Blue-Chip Bundle | US$39 | **2/10** | **US$39 的商品,37.5% 的字元是中文**(體檢卡的年度極值/腰斬史/股利/崩盤韌性全是逐字中文 claim);xlsx 表頭全中文;listing **無任何免責**。這不是「英文版」,是中文版加了一張英文封面 → 上架 Gumroad 幾乎必然退款+負評。 | + +**分數一行**:M2 8|C1 6|M1 5|T2 5|C2 5|T1 4|C1_en 3|C2_en 2 + +**英文自然度結論**:英文**原生文案**(封面標題/描述/section head/免責段)品質良好、無機翻感;問題**不是翻譯品質,是翻譯覆蓋率**——所有從資料層逐字帶出的內容(個股名、體檢 claim、來源註)與所有共用框架字串(頁碼、頁尾、免責標題、xlsx 表頭)**完全沒有英文路徑**。 + +--- + +## 問題排序 + +### BLOCKER(不能上架) + +1. **全 8 支 PDF 每頁後跟一張空白頁**(C2 @NT$1280:18 頁 9 頁空白;免費 M2 是漏斗第一印象也中)。根因=`.page` 用 A4 幾何但無 `@page` 規則 → Chromium 退回 Letter,每頁溢出 50pt。已在 scratchpad 證明 `format="A4"` → 空白頁 0。 +2. **C2 封面「每檔維度 11 項」對 3/9 檔不實**(國泰金 10、00878 7、國巨 6)——最高價 SKU 的**對外不實宣稱**,且 gate 結構上抓不到(非百分比宣稱)。 +3. **英文版 C1_en/C2_en 中文殘留**(C2_en PDF 37.5% 中文;兩者 xlsx 表頭/分頁名 100% 中文)——不能收 US$35/39。 +4. **8/8 `_provenance.json` `records=[]`**——`prov.num()` 呼叫數 0(v1=15),直接違反 `REDESIGN_SPEC_product.md:263` 且是 line 20 明列「不可退化」的 baseline 誠信面。**(數字本身無造假,已獨立重算 8,940 格零誤差;壞的是稽核存證。)** + +### MAJOR(該修) + +5. **T1 xlsx 第 3 列平均成本算出 92.5**(成交價空白 → `D3*E3`=0)——付費商品開檔即見錯誤數字。 +6. **T1 只有第 2/3 列有公式**,第 4 列起不自動算,與「每月登一列自動算」賣點不符。 +7. **C2 xlsx 最大回撤色階反向**(最深 -98.5% 塗紅=該表自訂的「好」色;同表 C 欄紅=最好)——買家讀反風險。 +8. **M1 listing「每日更新/附今日快照」但資料停在 07-13**(落後 3 天,缺 0714/0715/0716;來源抓取器疑似已停)——防呆清單過期有實害。 +9. **C1 無方法揭露**:「自適應」未定義、無參數/手續費/滑價/倖存者偏誤,xlsx 無欄位說明表 → NT$990 黑箱。 +10. **C1_en/C2_en listing 無免責**,而其 `seo_note` 自稱「含免責」(存證欄位本身不實)。 +11. **T2 硬編碼 11 項是潛在地雷**:`build_T2` 取 `codes[0]` 動態挑股,輪到 2882/00878/2327 就會出貨不實宣稱,gate 攔不到。 +12. **T2 listing「任一覆蓋權值股」但固定出貨鴻海**,買家不能選。 + +### MINOR(可上架後改) + +13. `2327` 名稱「**國巨\***」星號原樣輸出到 C2 xlsx/PDF(來源 `by_code` 即帶星號),買家會困惑。 +14. T1 `A6` 把說明文字放在資料列裡(使用者往下登資料會撞到)。 +15. C2 標題稱「**權值股**」合輯但含 `00878`(ETF)與 `2408` 南亞科,名實略有出入。 +16. C1_en `"a few winners don't make a strategy universal — judge by the median."` 小寫開頭、句構斷裂。 + +--- + +## 值得肯定(即使找碴視角也必須誠實列出) + +1. **數字零造假**:約 8,940 個獨立重算,**0 不符**。C1 1770 檔 × 5 欄逐格對 CSV 全中;M1 21 碼逐碼相同。 +2. **單位攻擊沒有第二顆雷**:`latest_dividend_yield` 的百分比判定正確(跨股量級反證:陽明 8.23 若當比率=823% 荒謬);所有 ratio 欄位該 ×100 的都乘了。 +3. **fail-closed gate 真的有接上**(`main():933-939` 見 `bad` 即不出檔),不是裝飾。 +4. **不編造缺失值**:`00878` 無估值/殖利率 → 留空,沒有填假數。 +5. **誇大詞 8/8 零命中**;中文版免責與「介紹≠推薦」6/6 齊全;C1 摘要主動寫「少數幾檔亮眼不代表策略通用——看中位數」、明標「2026-06-12 靜態快照,非即時可交易訊號」——這是**反著自己利益講話**,難得。 +6. **前導零全數保住**(00878/059570/911608)。 +7. **M2 的視覺與內容水準是真的到位**(見報告內渲染圖評述),當免費磁鐵綽綽有餘。 diff --git a/docs/ecommerce/VERIFY_REPORT_phase3c.md b/docs/ecommerce/VERIFY_REPORT_phase3c.md new file mode 100644 index 0000000..0f565cb --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_phase3c.md @@ -0,0 +1,70 @@ +# Phase 3c 交叉驗證報告 — 漏斗門面(Task #5 / funnel-face 交付) + +> 驗證者:spec-business(未參與 Task #5,獨立交叉驗)|日期:2026-07-16 +> 模式:找碴式(試圖證明做錯)|唯讀+本機,未打外部網路、未動 .env +> 驗證對象:`make_landing.py` + `assets/landing/index.html`、`listing_templates.py` + 9 份 listings_copy、`tg_magnet.py` diff、5 張 pinterest pin + +## 總評:PASS(5/5 檢查面通過)|0 BLOCKER|0 MAJOR|2 MINOR + +funnel-face 的交付誠信與一致性紮實:定價全對齊 config、數字宣稱全部對得上實際成品/資料、 +零誇大詞、兩套 listing 不打架、tg 磁鐵讀真檔、RWD 不爆版。只有 2 個 MINOR(都非阻斷,建議上線前順手修)。 + +--- + +## 檢查面逐項 + +### 檢查 1 — 再生穩定性(WYSIWYG / drift):PASS +- 親跑 `python make_landing.py` 重產 index.html → **MD5 前後完全相同**(`603a43aa159ad707819961bcb174de07`),`diff` 空 → 生成器與產物真的 WYSIWYG、無 drift。 +- **placeholder 數 = 4**(grep `PLACEHOLDER`),與宣稱一致。 +- **零商業真連結**:grep portaly/gumroad/shopee/whop/lemonsqueezy 的 http 連結 = 0;商品按鈕走 placeholder。頁面上的真連結只有既有聯盟/社群(Pionex/Perplexity/TradingView/YouTube/tg bot),非本次金流管道,符合預期。 +- 註:index.html 為 funnel-face 的工作區變更;我的重產與其位元組相同,故**不需 git checkout 還原**(還原反而會誤revert funnel-face 的變更)。 + +### 檢查 2 — 文案宣稱 vs 成品一致 + 兩套 listing 打架:PASS(1 MINOR) +**定價**(9 份 listings_copy vs `ecommerce/config.py`):全對齊,零矛盾。 +| SKU | copy 標價 | config | | +|---|---|---|---| +| T1 | NT$99 / US$5 | 99 / 5 | ✓ | +| T2 | NT$149 / US$7 | 149 / 7 | ✓ | +| C1 | NT$990 / US$35 | 990 / 35 | ✓ | +| C2 | NT$1280 / US$39 | 1280 / 39 | ✓ | +| 訂閱 | NT$99/149/1290 | basic99/full149/annual1290 | ✓ | + +**數量宣稱 vs 真實資料/成品**: +- C1「1770 檔」→ xlsx 實際 1770 資料列(親開 openpyxl 驗)✓ +- 訂閱「每週掃 1900+ 檔」→ state.json universe=1925 ✓;「約 1000 檔強弱榜」→ wave_top=1001 ✓;「估值約 1078 檔」→ 最新 valuation=1078(精準)✓;「34 板塊」→ sectors=34 ✓ +- C2/T2「20 年」、T1「10 年」→ 對齊體檢事實 long_horizon 20 年 / three_way 10 年 ✓ + +**兩套 listing 打架檢查**(funnel-face `listings_copy/*.md` vs 我 product_factory 產的 `*/listing.json`,同 SKU): +- **定價完全相同**(兩者都讀同一份 config)→ 無矛盾。 +- 名稱/內容物描述用詞不同但**指向同一商品、同一規格**(1770檔/含息還原20年/多空+Sharpe/估值位階…)→ 買家看兩版不會覺得被騙。 +- **結論:兩套 listing 不打架。** +- **[MINOR-1]** C2 copy 寫「EPS 趨勢圖端點為真實年度值,中間為示意序列並已標注」,但實際 product_factory_v2 的 EPS/營收/毛利 sparkline 用的是**完整真實年度序列**(非只端點),此 caveat 是沿用產品篇 mockup 的舊限制、與實際成品不符。方向是「少講」(under-promise)不構成欺騙,但建議修正措辭以精準對應成品。 + +### 檢查 3 — 誇大詞與誠信:PASS +- 掃 15 個誇大詞(穩賺/保證/翻倍/財富自由/年化必達…)於 9 份 copy + landing:唯一命中「保證」**全部是「不保證收益 / 歷史數據非未來保證」的否定用法**(逐一看 context 確認),非誇大,反而是誠信揭露。 +- 「介紹 ≠ 推薦」+ 免責:9 份中文 copy + landing **每處都在**;4 份英文 copy 有等義「Description ≠ recommendation / not investment advice / not a recommendation」。 +- pin 圖(親看旗艦+數據兩張):數字皆有據(34 板塊、1770 檔、NT$99–149、NT$990 全對),旗艦 pin 帶「介紹 ≠ 推薦」、數據 pin 帶「歷史快照,非即時、非可交易訊號」——**免責做進圖裡**,無誇大。 + +### 檢查 4 — tg_magnet:PASS +- `git diff` 讀畢(+72/-4):新增 `_daytrade_magnet()` **真讀 `twdata/daytrade_eligibility_*.json` 最新檔**(非寫死樣本)——smoke run 實跑吐出「資料日 2026-07-13、處置股 21 檔」與該日檔一致;讀不到檔 fail-safe 退回純防呆清單,永遠有內容。 +- `_PORTALY_SUBSCRIPTION_URL` 已定義(line 98,env 讀取,預設 `[PORTALY_URL_PLACEHOLDER]`);`_subscribe_cta()` 未設時**落回 landing**、不外發假訂閱連結 → placeholder 機制未破壞。 +- `_TWDATA = ROOT.parent/"twdata"` 路徑解析正確(ROOT=youtube_channel)。 +- `python -m py_compile tg_magnet.py make_landing.py listing_templates.py` → 全過。 +- 磁鐵名單只列真實代號、明寫「不是選股名單、不喊買賣」→ 誠信 OK。 + +### 檢查 5 — RWD 抽驗(375px):PASS +- Playwright 375×812 開 index.html:`scrollWidth==clientWidth==375`(**無水平溢出、無爆版**)。 +- 全頁截圖親看:數據鋪商品卡(旗艦訂閱三檔價/免費磁鐵/入門/數據包/EN pack)版面乾淨、字級與卡片自適應、底部免責完整。 + +--- + +## 跨切面發現 + +- **[MINOR-2] YouTube handle 不一致(非 funnel-face 之過,但上線前該收斂)**:全 repo `@carson-quant` 125 次 vs `@carsonquant` 5 次。landing/make_landing 用**多數派 `@carson-quant`**(與 repo 一致);少數派 5 處在 `ecommerce/config.py`、`subscription_report.py`、`finance_dept.py`(其他 agent 的檔)。→ landing 站在正確的一邊;建議 Carson 確認哪個是真實頻道 handle,並把落單的 5 處統一。**若 `@carson-quant` 其實是錯的,則升級為 MAJOR**(公開頁死連結),但證據(125:5)指向它是對的。 + +## 建議修正優先序(都非阻斷) +1. MINOR-1:改 C2 copy 的 EPS 示意序列措辭,對齊實際成品的完整真實序列。 +2. MINOR-2:確認並收斂 YouTube handle(以 landing 的 @carson-quant 為準,修 config/subscription_report/finance 的 5 處)。 + +## 驗證方法留痕 +- 重產 diff、MD5 比對、openpyxl 開 xlsx 數列、state.json/valuation 欄位核對、git diff 逐讀、py_compile、tg_magnet smoke run 讀真檔、Playwright 375px 溢出量測 + 截圖親看、pin 圖親看。全程唯讀本機、未打外網、未動 .env。 diff --git a/docs/ecommerce/VERIFY_REPORT_runbook.md b/docs/ecommerce/VERIFY_REPORT_runbook.md new file mode 100644 index 0000000..1bd53cb --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_runbook.md @@ -0,0 +1,159 @@ +# VERIFY_REPORT_runbook — GO_LIVE_RUNBOOK 找碴驗證 + +> 驗證人:verifier-sku2(fresh-context,未參與手冊撰寫)|日期:2026-07-16 +> 對象:`docs/ecommerce/GO_LIVE_RUNBOOK.md`(Carson 本人照抄執行的上線手冊) +> 立場:**一律以 repo 程式碼實況為準,不以手冊自稱為準**。行號逐一 Read 該檔該行;機制類宣稱用實跑驗證。 +> 唯讀:未改手冊、未開帳號、未打外部網路、未讀任何 .env 密鑰值(只掃程式碼的 getenv 讀取點)。 + +## 總結 + +| 檢查面 | 結論 | +|---|---| +| 1 env 變數總表 | **FAIL** — 變數名/行號 20/20 全對,但 `quant-service/.env` **沒有任何程式讀它**,且 §2.3 漏一個讀取點 | +| 2 行號/符號引用 | **FAIL** — `app.py` **6/7 個行號全部腐爛(整體 +14)**;其餘 30+ 個引用全對 | +| 3 webhook URL 路徑 | **PASS** — 四路徑 + `?token=` + `/health` 全部正確 | +| 4 placeholder 替換點 | **FAIL** — 落點 6/7 正確且無漏列,但 §4 的**替換機制本身失效**,且有 1 個假條目 | +| 5 SKU 上架對照表 | **PASS** — 8/8 目錄 + 點名檔名 + 定價 + 4 份 checklist 全部存在 | +| 6 依賴順序 | **PASS** — 無死結;1 個過時註記 | +| 7 有無腦補 | **PASS** — 不確定處全標「以官方為準/待確認」,且與程式碼註解一致 | + +**4 PASS / 3 FAIL。2 個 BLOCKER 都是「照做會靜默失敗」型:照手冊設完密鑰,webhook 收不到任何一筆錢;照手冊換完 placeholder,landing 買鈕仍是死連結。** + +值得先講:**這份手冊的誠信面做得好**——Whop/Portaly 欄位未經證實處全標「待校準」、PayPal/玉山標「待確認」、平台後台一律「以官方為準」,且 §3.4 的「官方無第一手 spec」與 `normalize.py:153` 的程式碼註解**完全一致**,沒有對 Carson 講死不確定的事。壞的是**機制驗證**(.env 到底有沒有人載)與**行號時效**。 + +--- + +## BLOCKER(照做會失敗 / 收不到錢) + +### B1 `quant-service/.env` 沒有任何程式讀它 → 四平台 webhook 全回 503,一毛收不到 + +手冊 §0 / §2.1 / §2.2 / §2.3 叫 Carson 把 `GUMROAD_PING_TOKEN`、`PORTALY_WEBHOOK_SECRET`、`SMTP_USER/PASS`、`ECOMMERCE_DL_*` 全部填進 `quant-service/.env`。 + +**實況:整個 `quant-service/webhook/` 樹零個 .env 載入器。** +- 證據:`Grep path=quant-service/webhook pattern=dotenv|environ.setdefault|\.env` → **0 occurrences across 0 files**。 +- 證據:全 repo 掃「誰讀 quant-service/.env」→ **零命中**(`load_dotenv()` 只出現在 **v1 舊檔** `quant-service/webhook_server.py:28-30`、`notify.py:12-14` 等,v2 的 `webhook/` 套件從不呼叫)。 +- 實跑證明(清空環境變數後): + ``` + Settings.from_env() → gumroad_ping_token='' / portaly_secret='' / dry_run=True + ``` + `config.py:124-131` 只 `os.getenv`,檔案裡有值也進不了 process 環境。 + +**後果**:`verify.py:59/63` fail-closed → 每個平台 webhook 回 **503** → Gumroad/Portaly 重試數次後放棄 → **成交事件全丟、名冊不動、記帳不動**;且 `dry_run=True` → **交付信永遠不寄**。全部靜默(Carson 只會看到「沒人買」)。 + +**手冊自己標了 `(待確認:以你本機 webhook 啟動器實際載入方式為準)`——但 repo 裡根本沒有 webhook 啟動器**:掃 `*.bat/*.vbs/*.ps1/*.sh` 提及 uvicorn/webhook.app → 只命中 `quant-service/requirements.txt`(其餘全是 jarvis venv 的 site-packages 雜訊)。這個 hedge 指向一個不存在的東西。 + +**修法建議(擇一,交給實作者)**:(a) 在 `webhook/app.py` 或 `config.py` 頂端加 `_load_env()`(照 `local_cron.py:70-76` 同款逐行 `KEY=VALUE` 解析,讀 `quant-service/.env`);(b) 提供一支 `啟動webhook.bat` 先 set 再起 uvicorn;(c) 手冊改成明寫 `set KEY=VALUE` 逐條指令。**在修好之前,§2 的整張表對 Carson 是無效操作。** + +### B2 §4「設 `.env` 的 `PRODUCT_STORE_URL` 後重跑 `make_landing.py`」不會生效 → landing 買鈕留死連結 + +§4 表格第 157/158 列明寫:設 `.env` 的 `PRODUCT_STORE_URL`/`GUMROAD_STORE_URL` 後**重跑** `make_landing.py`。 + +**實況:`make_landing.py` 拿不到 `youtube_channel/.env` 的值。** +- `make_landing.py` 零 .env 載入器(`Grep environ.setdefault|load_dotenv` → **0**),import 只有 `json/os/sys/pathlib`(line 27-31),且在 **module 層**就讀掉: + - `make_landing.py:40` `PORTALY = os.environ.get("PRODUCT_STORE_URL", "[PORTALY_URL_PLACEHOLDER]")` + - `make_landing.py:41` `GUMROAD = os.environ.get("GUMROAD_STORE_URL", "[GUMROAD_URL_PLACEHOLDER]")` +- **唯一會載 `youtube_channel/.env` 的是排程器** `local_cron.py:70-76`(`ROOT = Path(__file__).resolve().parent.parent` → `ROOT/.env` = `youtube_channel/.env`,line 29 已驗),它只把 env 餵給**自己排的 job**。 +- **`make_landing.py` 不在 `deploy/crontab.txt` 裡**(grep 該檔只有 `tg_magnet.py`@94/96/163、`ecommerce_weekly.py`@176)→ 它**只會被手動執行** → 手動 `python make_landing.py` 的 shell 沒有那些變數 → 落回 placeholder。 + +**後果**:重跑後 `assets/landing/index.html:115/142/148/154` 仍是 `[PORTALY_URL_PLACEHOLDER]`/`[GUMROAD_URL_PLACEHOLDER]`,**四個購買按鈕全是死連結**,而腳本會正常結束、不報錯 → **靜默**。這正是 §4 這一節要防的事,照做卻會發生。 + +> 對照:`tg_magnet.py`(crontab:94/96/163)、`ecommerce_weekly.py`(crontab:176)跑在排程下,**會**吃到 `youtube_channel/.env` ✓。所以 §2.4 的變數對這兩支有效,對 `make_landing.py` 無效。**手冊沒有區分這件事**。 + +**修法建議**:`make_landing.py` 加 `_load_env()`,或手冊改成 `set PRODUCT_STORE_URL=... && python make_landing.py`。 + +--- + +## MAJOR(會浪費時間) + +### M1 `app.py` 6 個行號全部腐爛(整體 +14) + +| 手冊處 | 手冊寫 | 實際 | 該行現在是什麼 | +|---|---|---|---| +| §3.4 驗證 | `app.py:47` | **61** | `@api.get("/health")` | +| §3.1 路由 | `app.py:53` | **67** | `@api.post("/sale-ping/gumroad")` | +| §3.1 token 驗 | `app.py:58` | **72** | `token = request.query_params.get("token","") or x_ping_token` | +| §3.2 路由 | `app.py:63` | **77** | `@api.post("/sale-ping/lemonsqueezy")` | +| §3.3 路由 | `app.py:71` | **85** | `@api.post("/sale-ping/whop")` | +| §3.4 路由 | `app.py:81` | **95** | `@api.post("/sale-ping/portaly")` | + +**根因**:phase3a 的 B1 修把 `_json_or_400()` 插進 `app.py:29-40`(+ 空行共 14 行),其後所有行號整體下移 14。手冊是在該修之前寫的。 +**唯一沒壞的**:§3 的 `app.py:8`(uvicorn 指令)✓ 仍正確——因為它在插入點**之前**。這個 +14 的一致性反過來證明其餘引用當初是真的掃過的。 + +### M2 §2.3 SMTP 漏列讀取點,且指向的 .env 檔會讓**旗艦週報永遠寄不出去** + +§2.3「誰在讀」只列 `webhook/delivery.py:28/29` + `config.py:131`(**兩者行號皆正確** ✓),但漏了: +- **`quant-service/ecommerce/subscription_report.py:301-304`** 也讀 `SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS`——這是**訂閱週報的寄送路徑**(旗艦商品!)。缺憑證時回 `{"sent": False, "reason": "SMTP 未設定(SMTP_USER/SMTP_PASS)"}`。 + +**為什麼是 MAJOR 不是 MINOR**:§2.3 叫 Carson 把 SMTP 填 `quant-service/.env`;但這支是被 `ecommerce_weekly.py` 用的,而 `ecommerce_weekly.py` 跑在 **youtube_channel 排程**下(`crontab.txt:176`),吃的是 **`youtube_channel/.env`**。→ 只填 `quant-service/.env` 的話,**旗艦訂閱週報寄送永遠 skipped**,而 §7.4 只教他驗「交付信」有沒有寄達,驗不到週報這條。 + +### M3 §3 uvicorn 指令沒說要在哪個目錄跑(只有 repo root 能跑) + +手冊 §3:`uvicorn quant-service.webhook.app:app --host 0.0.0.0 --port 8021`。 + +實測(`uvicorn.importer.import_from_string` 逐一驗): +``` +cwd = D:\carson-agent (repo root) → ✓ 成功 +cwd = D:\carson-agent\quant-service → ✗ ModuleNotFoundError: No module named 'quant-service' +cwd = C:\Users\User → ✗ ModuleNotFoundError: No module named 'quant-service' +``` +手冊緊接著寫的是路徑 `quant-service/webhook/app.py`,Carson 很自然會 `cd quant-service` 再跑 → 直接失敗。 +> 附帶澄清(我原本以為是 bug,實測推翻):`quant-service` 含連字號**不影響**——PEP 420 namespace package + uvicorn/importlib 走字串式 import,連字號合法。指令本身**是對的**,只差沒寫執行目錄。 + +--- + +## MINOR(可上線後改) + +- **N1 §4 line 161 `gen_media_kit.py` 是假條目**:該表宣稱它「(含 placeholder,媒體包用)」、「換成→對應連結」。實況:全檔**零個** `[PORTALY_URL_PLACEHOLDER]`/`[GUMROAD_URL_PLACEHOLDER]`;它的 `PLACEHOLDER = "〔待補:Carson 從 YouTube Studio 後台填〕"`(`gen_media_kit.py:41`)是**YT 後台數據佔位**(訂閱總數/聯絡窗口),跟商店連結無關,也不走 env。Carson 會去找一個不存在的連結 placeholder。 +- **N2 §3.4「`config.py:51 SUBSCRIPTION_TIERS`」未加前綴**:實際是 `quant-service/webhook/config.py:51` ✓(行號正確),但 repo 有**兩個** config.py,且 `quant-service/ecommerce/config.py` 在 §2 脈絡也出現過 → 建議寫全路徑免走錯檔。 +- **N3 §5 註記過時**:「Task #4(product_factory v2)標記 in_progress」——Task #4 已 completed(現由 fixer-sku 修 SKU BLOCKER)。§5 所有路徑/檔名**現在全部存在且正確**(見下),但檔名仍可能隨 fixer-sku 收尾微調 → 維持「以最終產物為準」的但書是對的。 +- **N4 §6 具體數字無官方出處**:30% 終身 / 「多為秒過」/ ClickBank cookie 60 天 / 蝦皮 NT$500·cookie 7 天 / 通路王「近 2 個月」——這些**忠實轉述自** `affiliate_checklists/*.md`(`tradingview_..:3,22`、`ichannels_..:3,10,25`),手冊沒捏造;但**checklists 自身也沒標官方出處**。建議 §6 補「方案條款以官方最新公告為準」。 + +--- + +## 各檢查面證據明細 + +### ✅ 檢查面 3:webhook URL 路徑 — PASS +四條路徑對 `app.py` 實際註冊全部正確:`/sale-ping/gumroad`(67)、`/sale-ping/lemonsqueezy`(77)、`/sale-ping/whop`(85)、`/sale-ping/portaly`(95)、`/health`(61)。 +Gumroad 的 `?token=` 寫法**正確**:`app.py:72` `request.query_params.get("token","") or x_ping_token` → query 或 `x-ping-token` header 皆可,與手冊 §3.1 描述一致。§2.1「密鑰未設 → 503」亦屬實(`verify.py:59/63`)。 + +### ✅ 檢查面 5:SKU 上架對照表 — PASS +8/8 目錄存在;點名檔名全中(`台積電體檢報告.pdf`、`台股定投追蹤模板.xlsx`+`_導引.pdf`、`台股全市場回測_1770檔.xlsx`+`台股全市場回測數據包_摘要.pdf`);`listings_copy/portaly/{C1_fullmarket_backtest,C2_bluechip_checkup,subscription_weekly}.md` 全在;4 份 affiliate checklist 全在。 +定價與 `ecommerce/config.py` 逐項相符:T1 99 / T2 149 / C1 990·US$35 / C2 1280·US$39(`ONE_OFF`)、訂閱 99/149/1290(`SUBSCRIPTION`)✓。§2.2「訂閱 SUB_weekly 無 dl_env」亦屬實(`webhook/config.py:46` `"dl_env": ""`)。 + +### ✅ 檢查面 6:依賴順序 — PASS +無 A 需要 B 產物但 B 排在 A 後的死結。「先上架拿下載連結 → 回填 `ECOMMERCE_DL_*`」(§5→§2.2)順序正確且兩處互相呼應。§8 依賴速查與各節一致。 + +### ✅ 檢查面 7:有無腦補 — PASS +§1.1/1.2/1.3/1.4「以官方為準」、§1.5「(待確認)」、§3.3「欄位待真實 webhook 校準」、§3.4「官方無第一手 spec,全為暫定」、§6「送件入口以官方為準」、§0「(待確認:webhook 啟動器載入方式)」——**不確定處全部標示**。 +且 §3.3/§3.4 的校準說明與程式碼註解**逐字對得上**(`normalize.py:7-11` 校準註記、`:153` 「官方無第一手 webhook spec(僅 n8n 教學證實 webhook 存在且含 姓名/email)」)→ 不是腦補,是忠實轉述。 +(唯一可再收緊:§1.1「自動續訂+自動發票」、§1.2「蝦皮數位交付走買家下單→你發下載連結」是平台機制宣稱,同段雖有「以後台實際欄位為準」但未直接涵蓋這兩句 → 建議補一句但書。) + +### ✅ 行號正確的部分(對照組,證明手冊當初真的掃過) +- `webhook/config.py`:35 / 38 / 41 / 44(`ECOMMERCE_DL_*` dl_env)、51(`SUBSCRIPTION_TIERS`)、124–131(六把密鑰 + SMTP dry_run)——**11/11 全對** +- `normalize.py`:7-11(校準註記)、46(`parse_gumroad`)、82(`_LS_KIND`)、118(`_WHOP_KIND`)、140-146(Whop 候選鍵)、152-154(Portaly 暫定註記)、155(`_PORTALY_STATUS_KIND`)、181-187(Portaly 候選鍵)——**8/8 全對** +- `delivery.py`:26 / 27 / 28 / 29(SMTP_HOST/PORT/USER/PASS)——**4/4 全對** +- `tg_magnet.py`:46 / 82 / 96 / 98、`autopost.py`:43 / 44 / 54 / 145、`make_landing.py`:40 / 41、`assets/landing/index.html`:115 / 142 / 148 / 154——**14/14 全對** +- §2.9「`weekly_report_v2.py` 本身不讀任何 env」——**實查 0 個 getenv/environ,宣稱屬實** ✓ + +--- + +## 回報用清單 + +### 「程式碼在讀、但手冊沒列」的漏網 env +**變數名層級:零漏、零多餘。** 手冊列的 20 個變數(6 把密鑰 + NTFY_TOPIC + SMTP×4 + ECOMMERCE_DL×4 + 漏斗 8 個)**全部真的有人讀,且行號全對**;無任何已從程式碼消失的殭屍變數。§2.5 的自我盤點屬實。 + +**讀取點層級:漏 1 個(見 M2)** +- `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` @ **`quant-service/ecommerce/subscription_report.py:301-304`** — §2.3「誰在讀」未列;且該路徑吃的是 `youtube_channel/.env`,與 §2.3 指定的 `quant-service/.env` 不同 → 旗艦週報寄送會靜默 skipped。 +- (`TELEGRAM_BOT_TOKEN` @ `subscription_report.py:325` 退回別名 —— §2.5 已註明,**不算漏** ✓) + +### 行號腐爛清單 +| 手冊 | 實際 | 位置 | +|---|---|---| +| `app.py:47` | **61** | `/health` | +| `app.py:53` | **67** | `/sale-ping/gumroad` | +| `app.py:58` | **72** | Gumroad token 驗 | +| `app.py:63` | **77** | `/sale-ping/lemonsqueezy` | +| `app.py:71` | **85** | `/sale-ping/whop` | +| `app.py:81` | **95** | `/sale-ping/portaly` | + +全部 **+14**,單一根因 = phase3a B1 修新增 `_json_or_400`(`app.py:29-40`)。`app.py:8` 未受影響仍正確。**其餘 37 個行號引用(normalize/config/delivery/tg_magnet/autopost/make_landing/index.html)全部準確。** diff --git a/docs/ecommerce/VERIFY_REPORT_weekly_fixes.md b/docs/ecommerce/VERIFY_REPORT_weekly_fixes.md new file mode 100644 index 0000000..9235f4e --- /dev/null +++ b/docs/ecommerce/VERIFY_REPORT_weekly_fixes.md @@ -0,0 +1,207 @@ +# VERIFY_REPORT_weekly_fixes — 週報續訂殺手修復 找碴驗證 + +> 驗證人:verifier-sku2(fresh-context,未參與本次修復)|日期:2026-07-17 +> 對象:工作區改動(HEAD=2ebbb74 未提交)——`weekly_report_v2.py`、`test_weekly_report_v2.py`、`test_weekly_review_fixes.py`(新增) +> 立場:**逐項試圖打破它的宣稱**。重算一律自寫 Python 直讀來源檔;gate/突變測試必須 import 受測引擎(那正是待測物)。 +> 唯讀:未改任何程式/產物、未打外部網路(引擎現算一律注入 stub)、未動 .env。 +> 重現:`scratchpad/attack_s6_gate.py`、`attack_s7.py`、`mutate_tests.py`、`adjudicate_s1.py` + +## 總結 + +| 項目 | 結論 | +|---|---| +| ① S7 點播(5 路徑) | **PASS**(1 個宣稱用詞不精確,非缺陷) | +| ② 「代號 代號」= 0 | **PASS** — 且零錯配 | +| ③ S8 真輪替 + 誠實揭露 | **PASS** — 主題 1/2 數字經獨立重算全中 | +| ④ S1 拒做 | **理由部分不成立** —— 見下方獨立裁決 | +| ⑤ S6 重定位 / 守門 | **PASS** — 三條件全成立,守門**未**被弱化 | +| ⑥ S5 產業集中度 | **PASS** — 數字與我重算一致 | +| ⑦ pytest 92 + 非空測 | **PASS** — 5 個突變全數變紅 | + +**0 BLOCKER / 0 MAJOR / 4 MINOR。總判定:可以 commit。** + +這批修復的品質明顯高於前幾批:**它在兩個可以「便宜過關」的地方選了難的正解**(S6 不灌 95 進池、S8 承認月度輪替不能讓每週不一樣),而且**自己把第一版空測抓出來改掉**。我用 5 個突變去打,全部被測試接住。 + +--- + +## ⑤ 守門有沒有被弱化(最嚴重的可能後果 —— 三條件全查) + +它為了讓 S6 印出「95% 信賴區間」,**可以**選擇把 95 灌進池(最省事),那會讓日後捏造的「勝率 95%」撞到鄰居而放行。它沒有,改用斷句避開 FG。**三個條件必須同時成立,我逐一實測**: + +| 條件 | 結果 | 證據 | +|---|---|---| +| 1. S6 自己過 gate | ✓ | `degraded=False, gated_out=None` | +| 2. 池裡**沒有** 95 | ✓ | 池中落在 95±0.25 的值 = `[]`;池全內容 = `[0.6, 0.61, 3.0, 5.5, 5.52, 5.5203…, 7.77, 7.8, 15.4, 15.8, 16.7, 19.0, 37.5659…, 37.57, 37.6, 278.0]`(只有 CI 端點 5.5/37.6 —— 那**本來就是**由來源算出的真值,該入池) | +| 3. 捏造「勝率 95%」仍被擋 | ✓ | 注入 `本策略勝率 95.0%` → `degraded=True, gated_out=True` | + +補強:對 S6 池丟 95/87.3/45/99/60 → **五個全擋**。池只有 16 個值,**鑑別力沒被稀釋**。 + +**一句話:守門沒有被弱化,而且它是在「有便宜路可走」的情況下選了正解。** + +附:`wilson_ci` 不是自創統計 —— k=50,n=100 → `(0.4038, 0.5962)`,**與教科書值逐位吻合**;k=3,n=19 → 5.5%~37.6%,與它宣稱一致;n=0 → `None`。 + +--- + +## ① S7 點播 —— 5 條路徑實測 + +| 路徑 | 結果 | +|---|---| +| 空佇列 → 引擎零呼叫 | ✓ `calls=0` | +| 空佇列 → 溯源/池不變 | ✓ 溯源 71=71、池 819=819 **逐值相同** | +| 點播庫內檔(2412) | ✓ 排第一 `['2412','2330','2317',…]`、有「訂戶點播」徽章、**引擎零呼叫**、佇列 `status=fulfilled` | +| 點播不在庫(9999) | ✓ 呼叫引擎一次、**沒有編造體檢卡**、佇列保留 `pending`、`attempts=1`、有告知訂戶 | +| 引擎拋例外 | ✓ **不炸**(`degraded=False`,其餘 9 張卡照常)、`last_error` 記錄、仍 `pending` | +| 連續失敗 3 次 | ✓ `attempts 2→3, status=failed` | + +**佇列 schema 攻擊**:`_RX_CODE = ^\d{4,6}[A-Z]?$` 擋掉路徑穿越/注入;code 還要通過 twstock 全市場名冊比對;email 必須在 `load_send_list("full")` 名單內(fail-closed:0 訂閱 → 任何點播都不受理)。額度在 `submit_request()` 就擋,不是產報告時才發現。**打不破。** + +### `[MINOR-1]` 「空佇列逐字相同」這個用詞不精確(非缺陷) +我把 HEAD 版取出來同資料對跑,**HTML 不是逐字相同**(11319 → 11403 字元): +- 標題 `本週深度體檢個股` → `本期深度體檢個股` +- lead 多一行 `完整版訂戶每月可點播 1 檔指定個股——想看自己手上那檔,不用等輪到。` + +**但這兩處都是刻意且正確的改動**:標題改「本期」是**修正**(點播檔可能不是本週算的,再叫「本週」反而不實);多的那行是刻意的 upsell。 +**真正該宣稱的是**:「**資料/溯源/引擎呼叫行為完全等價**(溯源 71=71、池逐值相同、引擎零呼叫),渲染文案有兩處刻意變更」。測試本身的斷言是**對的**(它比的是 `None` vs `[]` 兩條路徑,不是新舊版),沒有說謊 —— 只是轉述到我這裡時「逐字相同」這個詞蓋過了範圍。 + +--- + +## ② 「代號 代號」殘留 = 0,且**零錯配** + +| 產物 | 「代號 代號」次數 | +|---|---| +| `weekly_2026-07-16_*.pdf`(修復前舊產物) | **12** `[1102,1423,1432,1446,2356,2387,2536,2891,3045,4137,5225,5706]` | +| `weekly_2026-07-17_*.pdf`(修復後重產) | **0** ✓ | + +**反向攻擊(名稱錯配到別檔,比印兩次更糟)**:抓修復後 PDF 的「名稱+代號」組合去對 twstock 權威表 → **108 組,0 不符**。我原本報的 12 檔現在全部正名:2891→中信金、3045→台灣大、1102→亞泥、2356→英業達、1432→大魯閣、1446→宏和、2387→精元、2536→宏普、5225→東科-KY、4137→麗豐-KY、1423→利華、5706→鳳凰 —— **逐一與 twstock 相符**。 + +> 誠實補充:我第一版掃描把新舊產物一起 glob,誤報「仍有 12 處殘留」;又把 S7 內文的日期(`一路等到 2010-11-03`)與百分比(`報酬 6414.2%`)誤判成「名稱錯配」。**兩個都是我的抽取器 bug,已修正後重測,未列為缺陷。** + +`_twstock_meta()` 走**本地套件內建表、不打網路**,失敗回空 dict 由既有三層來源兜底 —— 沒有引入外部依賴風險。 + +--- + +## ③ S8 真輪替 + 誠實揭露 + +**輪替是真的**:12 個月實跑 → 主題編號用到 `[0,1,2]` 全三個、**相異 hash 恰好 3 個**(一主題一個);月內穩定(`2026-07-01` 與 `2026-07-28` hash 相同 = `4480440abc`)。 + +**主題 1/2 的數字是真來源、不是硬編 —— 我獨立重算逐欄對照**: + +| 主題 | 成品綁定 | 我獨立重算 | +|---|---|---| +| 1 多空(`longshort_per_stock.csv`) | n=1770、純多中位 **-1.095**、多空中位 **-2.66**、加空變好 **615**(**34.746%**) | n=1770、-1.10、-2.66、615、34.7% ✓ **全中** | +| 2 風險(`per_stock_results.csv`) | n=**3682**、sharpe 中位 **-0.073**、dd 中位 **7.92** | n=3682、-0.073、7.92 ✓ **全中** | + +**誠實揭露(我原判「對訂戶不實」的核心)—— 現在是真話**: +> 「這段的更新規則(先講清楚,免得你以為我們在灌水):本段是教育性基準,**每月換一次主題**(共 3 個主題輪替),**同一個月內的每週內容相同**——它要傳達的是不隨盤勢變動的結構性事實,不是每週追新聞。資料為 **2026-06-13** 的靜態快照(**距今 34 天**),非即時、非可交易訊號。」 + +標題也從無條件的「結構基準(月度輪替)」改成具體主題名(`結構基準 · 本月主題:加了放空會更好嗎`)→ **不再有「輪替」這個當時是假話的宣稱**。 +`snapshot_age_days=34` 有綁 provenance。缺檔時三個主題各自 `degraded` 降級(已驗)。 + +> 它在註解裡明講「月度輪替**並不能**讓每週不一樣……undisclosed 的重複才是退訂觸發器,disclosed 的月更節奏不是」——**這個判斷我同意**,而且它沒有假裝解決了它沒解決的事。真正的解(每月重跑回測)它標為需另開 ops task,沒有偷偷跳過。 + +--- + +## ⑥ S5 產業集中度 + +成品:「本期 Top 15 的產業分布集中在 **建材營造業 6 檔、紡織纖維 2 檔**(共 15 檔)」 +我獨立重算(自取 Top15 殖利率 → 查 twstock 產業):`{建材營造業: 6, 紡織纖維: 2, 其他電子業: 2, 運動休閒: 1}` → **完全一致** ✓ + +**沒有變成買賣建議**:全文含「只陳述位置、**不推薦任何個股**」「介紹 ≠ 推薦」,且把機制講白(殖利率=回頭看的數字/一次性獲利認列完會掉/股價跌也會讓殖利率變高)。這正是我原本要的「揭露而非建議」。段落過 gate(有測試釘死)。 + +### `[MINOR-2]` 「紡織纖維 2」顯示了但沒綁 provenance +`_bind` 只綁了 `top_ind[0]`(建材營造業 6),文案卻印出兩個產業。「2」不是 % 宣稱故 gate 本就不管,不影響 fail-closed —— 但屬「呈現了卻沒進存證」的殘餘(與我在 c9538e6 報告列的封面「1001 檔/34 類」同型)。 + +--- + +## ⑦ pytest 92 + 突變測試(它自承第一版是空測) + +`pytest quant-service/ecommerce/tests -q` → **92 passed**(宣稱 92,屬實)。 + +**我另外做 5 個突變(runtime patch,不改 repo 檔),打壞行為看測試會不會變紅**: + +| 突變 | 對應測試 | 結果 | +|---|---|---| +| 空佇列硬塞一檔點播(**就是它自承第一版抓不到的那種**) | `test_s7_empty_queue_is_identical_and_calls_no_engine` | 🔴 變紅 ✓「佇列空(None)卻出現點播徽章」 | +| 點播完全失效(忽略佇列) | `test_s7_request_actually_reorders_vs_empty_queue` 等 2 個 | 🔴 全變紅 ✓ | +| name map 退回代號當名字 | `test_name_map_never_falls_back_to_code` | 🔴 變紅 ✓ | +| S8 主題不輪替(永遠主題 0) | `test_s8_theme_rotates_monthly_and_is_deterministic` | 🔴 變紅 ✓ | +| **把 95 灌進 S6 池(弱化守門)** | `test_s6_passes_gate_without_pooling_confidence_level` 等 2 個 | 🔴 全變紅 ✓「95(信心水準)被灌進池 → 守門被弱化」 | + +**5/5 突變全被接住 —— 不是空測。** 空測那條它確實修好了:斷言從「兩條路徑自洽」改成「**沒有任何點播的痕跡**」這個絕對事實,這是正確的修法。 + +--- + +## ④ 獨立裁決:S1「溫度→歷史動作對照」拒做的理由成不成立 + +### 裁決:**拒絕「重建溫度」是對的;但「做不到誠實版」不成立 —— 有一條它沒想到的誠實路徑。** + +#### (a) 它說對的部分(我逐一查證,屬實) +- `state.json` **確實沒有溫度歷史**(頂層無任何 history/快照;只有當期 `gauge`)、無 `gauge_history.jsonl` ✓ +- `twdata/cache`(**1939 檔**)確實只有 **170 列 ≈ 8 個月**(2025-11-03~2026-07-16),但**有 Volume** ✓ +- `twdata/cache_adj` 確實**只有 `Date, Close` 兩欄** ✓ +- 「Close-only 變體重建今日 breadth **21.7** vs 官方 40.9」→ **我獨立重算也得到 21.7**,它這個數字是誠實的 ✓ +- **它自承的那個雷是真的、而且抓得漂亮**:重建溫度 45.2/45.5 vs 官方 45.4 看似完美命中、內部成分卻全錯。這正是「數值對得上≠方法對」的經典陷阱,**它沒有出貨這個版本,是正確的專業判斷**。這一點值得肯定,不是我要扣分的地方。 + +#### (b) 它說錯的部分(關鍵) +**它的「20 日前瞻僅 ~7 個獨立期,統計無意義」是對 `cache`(8 個月)算的 —— 但那是錯的資料集。** + +`gauge.components` 實際只有 5 個:`{rsi, breadth, adr, nhnl, vol}`。**只有 `vol` 需要 Volume;`rsi`/`breadth`/`adr`/`nhnl` 四個全部只需要 Close。** +而 `cache_adj` 有 **1923 檔**、抽樣列數中位 **4539 列**、最早起點 **2000-01-04**。 + +**我實際建了一條全市場歷史序列(只用 Close,`scratchpad/adjudicate_s1.py`)**: +``` +可用個股 = 1916 檔 (它宣稱重建時 n=125 —— 這個數字我複現不出來,應是它的篩選條件出了問題) +價格矩陣 = 8421 交易日 × 1916 檔 +breadth(站上20MA%)序列 = 6557 天(2000-01-31 ~ 2026-07-14) +前瞻對照可用樣本 = 4249 天 → 獨立(不重疊)20 日期數 ≈ 212 ← 不是 7 +``` +→ **資料深度足夠,誠實版做得出來。** 「做不到」不成立。 + +至於 21.7 vs 40.9 的落差:主因是 **`cache_adj` 止於 2026-07-14、官方 state 是 2026-07-16(落後 2 天)** + 含息還原價 vs 原始價的定義差。**但這個落差不構成障礙** —— 誠實版本來就不該去對官方溫度,而是**自成一套、標明定義**(「站上20MA%,以 cache_adj 含息還原收盤價重算,與 S1 當期 breadth 定義略有差異」),內部一致即可。 + +#### (c) 但我要誠實講完後半段:**這條路的答案不是我原本期待的** +我把它跑完了(0050 未來 20 日報酬 vs breadth 分位): + +| breadth 區間 | 樣本天數 | 獨立期 | 未來20日中位 | 上漲機率 | +|---|---|---|---|---| +| 0~20% | 363 | 18 | +2.39% | 64.2% | +| 20~40% | 1069 | 53 | +1.52% | 61.7% | +| 40~60% | 1437 | 71 | +1.49% | 61.7% | +| 60~80% | 1130 | 56 | +1.15% | 60.5% | +| 80~101% | 250 | 12 | +2.27% | 72.4% | +| **全期基準** | 4249 | 212 | **+1.53%** | **62.2%** | + +**訊號很弱**:中間三個區間(涵蓋 87% 的日子)的上漲機率 60.5~61.7%,與全期基準 62.2% **幾乎沒有差別**;只有兩個極端略高,而 80%+ 那格只有 12 個獨立期、不足以下結論。 + +→ **誠實版的誠實結論是:「市場溫度/廣度對未來 20 日大盤報酬幾乎沒有鑑別力」。** + +#### (d) 所以我的裁決三句話 +1. **它拒絕出貨假的溫度重建 = 專業判斷正確**,那個 45.2 vs 45.4 的陷阱抓得很好,這種自我糾錯應該被鼓勵。 +2. **它「資料做不到誠實版」的論證不成立** —— 用錯資料集(`cache` 8 個月 vs `cache_adj` 26 年)、把「不能重建 composite 溫度」誤推成「不能做任何誠實對照」。純 Close 可算的成分佔 5 個裡的 4 個,`cache_adj` 給得起 212 個獨立期。 +3. **但把這條路做完,S1 救不回「可行動」** —— 因為誠實的答案是「這個指標沒有前瞻 edge」。 + +#### (e) 對「basic 值不值得留」的影響(我原話是「S1 是唯一能救 basic 的一刀」) +**我修正我自己的判斷**:那一刀砍下去,砍出來的是「溫度沒有預測力」。 +- 作為**可行動洞見**:救不了 basic(4 分還是 4 分)。 +- 作為**內容**:這其實**很值錢**,而且完美對齊本產品已經證明能做到的調性 —— S6「我們的訊號是虧的」、S8「策略無腦套只有 53% 正報酬」,再加一段「**連我們自己首頁那個溫度計都對未來沒有鑑別力**」,三段合起來是台灣財經內容裡幾乎沒人敢做的**反直覺誠實三連**,也正是頻道「數字戳破直覺」的定位。 +- **建議**:把它做成 S1 的一個**固定小區塊**(「這個溫度能不能拿來擇時?26 年數據:不能」),而**不是**做成「溫度→動作對照表」(那張表誠實地做出來會是一張沒有差別的表)。 +- **basic 的定價結論不變**:仍建議砍掉或降 49 + 塞 1 檔 S7 當鉤子。S1 補上這段能從 4 分拉到 6 分(有記憶點、有誠信差異化),但撐不起 99。 +- **它建議的 `gauge_history.jsonl`(每日 scan 收尾追加)仍然值得做** —— 那才能對官方定義的溫度做對照;只是**不必等半年**才有東西可出,上面那條 Close-only 的路現在就能出。 + +--- + +## MINOR 清單 + +- **MINOR-1** 「空佇列逐字相同」用詞不精確:實際是「資料/溯源/引擎行為等價,渲染有兩處刻意變更(標題本週→本期、lead 多一行 upsell)」。程式沒問題,測試斷言也對,只是宣稱範圍該收斂。 +- **MINOR-2** S5「紡織纖維 2 檔」顯示了但沒綁 provenance(只綁了 top_ind[0]);非 % 宣稱故不影響 gate。 +- **MINOR-3** **舊產物未清理**:`weekly_2026-07-16_*.pdf/html/json`(**含 12 處「代號 代號」的壞版本**)與 07-17 新版並存於 `output/ecommerce_ready/weekly_v2/`。Carson 手動寄送時可能拿錯檔 → 建議產新版時清掉或移到 `_archive/`。 +- **MINOR-4** 點播佇列**無限增長**:`doc["requests"].append(...)`,fulfilled/failed 紀錄永久留存,長年累積會讓檔案變大(功能無誤,屬衛生問題)。建議保留最近 N 期或定期歸檔。 + +--- + +## 值得肯定(找碴視角也必須誠實列出) + +1. **在兩個「可以便宜過關」的地方選了難的正解**:①S6 不把 95 灌進池(灌了最省事,但會弱化守門)②S8 承認「月度輪替不能讓每週不一樣」並把規則講白,而不是假裝解決了。 +2. **自己把第一版空測抓出來並改對**——而且改的方向是對的(從「兩條路徑自洽」改成「沒有任何點播痕跡」的絕對斷言)。我 5 個突變全被接住。 +3. **④ 拒做時沒有硬幹**:寧可交白卷+附證據,也不出貨一個「數字對得上但方法錯」的假指標。它自陳的 45.2 vs 45.4 陷阱,是我在這整輪驗證裡看過最好的一次自我糾錯。**它的結論(不做)我維持;只是理由要更正。** +4. 所有新數字(S8 主題 1/2、S5 集中度、Wilson CI)**經我獨立重算全部命中**,零硬編、零造假。 diff --git a/docs/ecommerce/pinterest_setup_guide.md b/docs/ecommerce/pinterest_setup_guide.md new file mode 100644 index 0000000..ccfba2b --- /dev/null +++ b/docs/ecommerce/pinterest_setup_guide.md @@ -0,0 +1,96 @@ +# Pinterest 電商引流設定指南(Carson 親手照做版) + +> 目的:把 `pinterest_pin_generator.py` 產出的 pin 圖,透過 Pinterest 這個「數位商品最大免費站內流量管道」導向電商商品(S1-S3)。 +> **這份是給 Carson 照著做的步驟,不是自動化腳本。** Pinterest 是**新的對外發布管道**,依 CLAUDE.md 紅線:任何實際發布、連帳號、設排程工具帳密 → **一律 Carson 親手**,agent 只做到「產圖 + 這份指南」為止。 + +Pin 圖成品目錄:`quant-service/output/ecommerce_ready/pinterest/` + +--- + +## 為什麼是 Pinterest(一句話定位) +Pinterest 是**視覺搜尋引擎**,不是追蹤者 feed。一支 pin 靠 SEO 關鍵字被搜到,可以在發佈後**數週到數月持續**帶流量(長尾),對「30 訂閱、無現成流量」的起步階段最划算——這是它勝過 IG/Threads 的關鍵差異。 + +--- + +## 步驟 0:帳號申請(免費,約 15 分鐘) +1. 到 pinterest.com 註冊,或把個人帳號在設定裡**轉成 Business 帳號**(免費,解鎖 Analytics 與廣告後台,不轉就沒數據可看)。 +2. **Claim your website(認領網站)**:設定 → Claimed accounts → 貼上你的商品落地頁網域(Portaly/Gumroad 個人頁或自架 landing 都可),照它給的 meta tag / HTML 檔驗證。認領後你的 pin 會掛上你的品牌、流量歸戶。 +3. **開啟 Rich Pins**:認領網站後,商品頁若有 Open Graph meta,Pinterest 會自動抓標題/價格顯示成 Product Rich Pin(更可信、CTR 更高)。用 Rich Pin Validator 驗一次即可。 +4. 建 3-5 個**主題板(Boards)**,別全丟一個板。建議: + - 「台股定投 / 存股」 + - 「量化投資入門」 + - 「個股體檢報告」 + - 「投資避雷 / 新手常見錯誤」 + - 每個板寫**含關鍵字的板描述**(Pinterest 靠這個判定板主題)。 + +--- + +## 步驟 1:排程工具怎麼選 + +### 選項 A(推薦起步):Pinterest 原生排程器 — 免費 +- 建立 pin 時可選「**Publish later**」,一次最多排到 **30 天內**、**一次一支**。 +- 優點:零成本、零第三方風險、最不會被判 spam(官方自家管道)。 +- 缺點:一次只能排一支、要手動,量大時累。 +- **起步(前 4-8 週)就用這個**,先養帳號權重。 + +### 選項 B(規模化後):Tailwind — Pinterest 官方 Marketing Partner +- Tailwind 是 Pinterest **官方認證**的排程夥伴(不是第三方灰色工具),被判 spam 的風險低。 +- 核心功能:**SmartSchedule**(自動排在你受眾最活躍的時段)、**Interval Pinning**(同一支 pin 隔數天才發到不同板,而非一次全發 → 這正是避免 spam 誤判的關鍵設計)、批次上傳。 +- 免費方案有額度(足夠起步驗證),量大再升月費方案。 +- **KYC/帳密由 Carson 本人綁**,agent 不碰。 + +> 不要用來路不明的「免費自動發文機器人」:Pinterest 對非官方 API 的自動化查得嚴,新帳號被封等於前功盡棄。要嘛原生、要嘛 Tailwind。 + +--- + +## 步驟 2:避免被判 spam 的節奏鐵律(新帳號尤其重要) + +Pinterest 對「短時間狂發、同圖洗版、連結全指同一頁」最敏感。照這些做: + +1. **暖機期(前 2-4 週)**:每天 **1-3 支** pin 就好,別衝量。新帳號一開就每天 20 支 = 高風險。 +2. **穩定期**:每天 **5-15 支**,**分散整天**發(用排程器攤開,不要同一分鐘連發)。 +3. **一支 pin 不要同時發到多個板**:要跨板就**隔 3-7 天**再發到下一個板(Tailwind Interval 就是做這個)。 +4. **多做 Fresh Pin**:Pinterest 現在偏好「新圖」。`pinterest_pin_generator.py` 對每個 SKU 產不同視覺,**同一商品可以換 headline/配色多產幾版**當不同 fresh pin,勝過重複發同一張。 +5. **連結別全部指同一頁**:混搭「免費工具落地頁 / 免費 YT 影片 / 商品頁」,不要每支 pin 都硬導購買頁,否則像廣告農場。導購與免費內容比例抓 **1:2 到 1:3**。 +6. **描述寫給人看、不要塞滿 hashtag**:2-3 個相關 hashtag 就好,關鍵字自然融進句子。 +7. **禁**:買讚/買追蹤、同帳號多開互導、把別人的圖改一改當自己的。這些一旦被抓,整個網域可能被降權。 + +--- + +## 步驟 3:pin 的 SEO(讓它被搜到,這才是流量來源) + +Pinterest = 搜尋引擎,標題與描述的**關鍵字**決定曝光: + +- **Pin 標題(≤100 字)**:放使用者會搜的詞。例:「台股全市場每週掃描|新手定投該看哪些數據」。 +- **Pin 描述(200-500 字佳)**:自然寫出「台股、定期定額、0050、個股體檢、回測、存股」等長尾關鍵字 + 一句 CTA(「免費領檢查表,連結在下方」)。 +- **誠信鐵則(承襲產線)**:描述**不准**出現捏造的報酬率/勝率/轉換率,不用「穩賺/保證/必賺」。守「介紹 ≠ 推薦」。pin 上的價格是真實售價,可以放。 +- Alt text 也填關鍵字(無障礙 + SEO 雙贏)。 + +--- + +## 步驟 4:發佈流程(Carson 每次照做) + +1. 跑產生器產最新 pin:`python youtube_channel/scripts/pinterest_pin_generator.py` +2. 到 `quant-service/output/ecommerce_ready/pinterest/` 取圖。 +3. 在 Pinterest(或 Tailwind)建 pin:上傳圖 → 填**標題/描述(照步驟 3 的關鍵字)** → 貼**目標連結**(該 SKU 的 Portaly/Gumroad 商品頁,或免費工具落地頁)→ 選對應主題板 → 選 Publish later 排程。 +4. 一支 pin 只填一個連結。跨板隔幾天再發。 +5. 兩週後看 Business Analytics 的 impressions / outbound clicks,把表現好的主題多產幾版 fresh pin,墊底的淘汰。 + +--- + +## 對照電商計畫的 pin → 連結去向 + +| Pin | 商品 | 連結去向(Carson 綁) | +|-----|------|----------------------| +| `pin_S1_台股訂閱週報` | S1 台股掃描+體檢週報訂閱 | Portaly 訂閱頁 / 免費工具落地頁 | +| `pin_S2_回測數據包` | S2 全市場回測數據包 | 蝦皮 / Portaly 商品頁 | +| `pin_S3_intl_workbook` | S3 國際英文數據包 | Gumroad 商品頁 | +| `pin_T1_個股體檢系列` | 頻道磁鐵(導 YT+訂閱) | YT 影片 / 訂閱落地頁 | +| `pin_T2_定投脈絡` | 頻道磁鐵(免費檢查表) | 免費檢查表落地頁 | + +--- + +## 紅線提醒(agent 不做、Carson 親手) +- 開通 Pinterest 帳號、認領網站、綁 Tailwind 帳密 KYC。 +- **首次實際發佈前,Carson 拍板**(新對外管道)。 +- agent 只負責:持續產 fresh pin 圖 + 維護這份指南。發佈與帳號操作**全程人工**。 diff --git a/docs/ops/DIAGNOSIS.md b/docs/ops/DIAGNOSIS.md new file mode 100644 index 0000000..64066b1 --- /dev/null +++ b/docs/ops/DIAGNOSIS.md @@ -0,0 +1,88 @@ +# Harness 快速診斷(2026-07-03,Fable 5 制度化 session) + +> 本檔是 `docs/ops/` 制度系列的地基:列出這個 harness 目前最漏 token、最容易失焦、最容易出錯的前三名,各附具體修法。其他制度檔(dispatch.md、judgment.md 等)的規則都是針對這三個病灶設計的。 +> 事實來源:2026-07-03 由兩個 Explore agent 實地盤點 `.claude/`、`D:\claude`、memory 目錄後確認,非憑印象。 + +--- + +## 第一名:固定 context 開銷 + 殭屍 hooks(最漏 token) + +### 症狀 +每個 session 還沒開始做事,開場就被塞入: + +- **301 個 skills 的完整清單**(含大量 claude-flow stock 文件型 skill,如 `sparc:*`、`swarm:*`、`hive-mind:*` 約 150+ 條,絕大多數從未被使用) +- **40+ 個 agent types**,原生可靠的主要是 `Explore`、`Plan`、`general-purpose`、`claude`(另有 `claude-code-guide`、`statusline-setup` 等特化原生型別);其餘大宗是 claude-flow / SuperClaude vendored 的 persona,品質未經驗證 +- **superpowers 的 using-superpowers skill 全文**經 SessionStart hook 注入 +- **MEMORY.md 索引 50+ 條**(精確閾值見 maintenance.md 第 4 節) + +### 根因 +1. 專案 `.claude/settings.json` 的 claude-flow hooks 全部指向 `C:\Users\User\.claude\helpers\hook-handler.cjs` — 這是**殭屍路徑**(junction 指到 `D:\.claude`,而真正生效的 config 目錄是 `D:\claude`,由 `CLAUDE_CONFIG_DIR` 環境變數確認)。這些 hooks 在每次 Bash/Edit/SessionStart/Stop 都嘗試執行。 +2. `.claude/agents/` 和 `.claude/commands/` 幾乎全是 claude-flow v3 vendored 內容,只有 `evolution.md` 和 `morning.md` 是 Carson 親寫。vendored 內容持續灌進每個 session 的可用清單。 + +### 具體修法(需 Carson 放行的列為建議,弱模型不要自己動) +| 修法 | 誰能做 | 動作 | +|------|--------|------| +| 拔掉專案 `.claude/settings.json` 裡的整個 claude-flow `hooks` 區塊(路徑已死,只剩開銷) | **Carson 放行後才能改**(settings 寫入常被分類器擋) | 刪 `hooks` 中所有指向 `C:\Users\User\.claude\helpers` 的條目;保留 `permissions` 區塊 | +| 移除 `.claude/commands/` 下的 claude-flow stock 目錄(agents/analysis/automation/coordination/github/hive-mind/hooks/memory/monitoring/optimization/sparc/swarm/workflows + claude-flow-*.md),只留 `evolution.md`、`morning.md` | **Carson 放行**(建議先搬到 `D:\claude-flow-archive\` 而非刪除) | 這一項砍掉開場 skill 清單裡約 150 條 noise | +| MEMORY.md 索引精簡:同主題多條合併、已淘汰專案(如 triple-supertrend)標註歸檔 | 弱模型可自做(閾值與程序見 maintenance.md 第 4 節,以該檔為準) | 超過 50 條時執行 | +| 不要在主對話讀大檔案(見第三名) | 弱模型每輪自律 | 見 dispatch.md | + +--- + +## 第二名:設定漂移 — 三個 config 目錄 + 過時 CLAUDE.md(最容易出錯) + +### 症狀 +弱模型會**自信地遵循錯誤資訊**: + +- 舊 CLAUDE.md 寫工作目錄是 `C:\Users\User\Downloads\carson-agent` → **實際是 `D:\carson-agent`** +- 舊 CLAUDE.md 寫預設模型 Sonnet 4.6 → 已過時 +- 舊 CLAUDE.md 把 `defaultPermissionMode: acceptEdits` 寫成散文 → 它不是真的設定,真設定在 settings.json 裡根本沒這個 key + +### 根因:三個 config 目錄並存 +| 路徑 | 狀態 | +|------|------| +| `D:\claude` | ✅ **唯一生效**(`CLAUDE_CONFIG_DIR` 環境變數,User+Machine 層都設了)。GSD hooks、Superpowers plugin、全域 settings.json 都在這 | +| `D:\.claude` | ❌ 殭屍(舊遷移殘留) | +| `C:\Users\User\.claude` | ❌ junction → `D:\.claude`,一樣是殭屍;但專案 settings.json 的 hook 路徑硬編指向這裡 | + +### 具體修法 +1. ✅ 已做(本 session):重寫 `CLAUDE.md`,環境事實區只寫已驗證的資訊。 +2. **規則(所有未來 session 遵守)**:任何檔案路徑,用之前先驗證存在(`Glob` 或 `Test-Path`),尤其是從記憶或舊文件裡讀到的路徑。路徑衝突時以 `CLAUDE.md` 環境事實區為準;CLAUDE.md 自己也錯時,以 `$env:CLAUDE_CONFIG_DIR` 實測為準,並回頭修 CLAUDE.md。 +3. **建議 Carson(需本人做)**:確認 `D:\.claude` 無獨有內容後整目錄改名為 `D:\.claude-DEAD`,讓殭屍引用快速炸出來而不是靜默半生效。 + +--- + +## 第三名:無派工與驗證紀律(最容易失焦) + +### 症狀 +1. **主對話自己下場**大量讀檔/掃 repo/查網頁 → context 塞爆 → 觸發 compaction → 早期指示被壓縮遺失 → 後半段失焦、重問已答過的問題。 +2. **自己寫自己驗**:改完就宣布完成,沒有獨立驗證。本 repo 真實事故: + - `queue_size` 把已發布舊片算進庫存 → 產線誤判停產多日(見 memory `yt-studio-production-halt-rootcause`) + - web_center 前端測試用 stub 攔錯層(攔 `window.api` 而非 `window.fetch`)→ 誤觸正式雲端 `schedule_publish`(見 memory `web-center-test-prod-misfire`) +3. **模型調度全憑當下心情**:沒有規則決定什麼活派 haiku、什麼活升 opus、錯了怎麼升級。 + +### 具體修法 +全部制度化在兩個檔: +- `docs/ops/dispatch.md` — 指揮官不下場、派工三件套、model/effort 顯式指定、回報合約、升降級路徑、驗證不自驗 +- `docs/ops/judgment.md` — 何時升級/何時算完成/何時該問/方向錯訊號/品質底線,附正反例 + +**一條先行鐵律(其他檔都引用它)**:凡是「對外發布、動錢、寫正式機」三類動作,執行前必須有一次獨立驗證(fresh-context agent),不論任務多小、不論有沒有授權 — 授權解決「能不能做」,驗證解決「做對了沒」,兩者不可互相替代(完整規則見 CLAUDE.md 紅線區)。上面兩個事故都是這條缺席造成的。 + +--- + +## 補充(2026-07-03 同日):弱模型長任務三大崩潰場景 → 防禦對照 + +上面三名是「結構性病灶」;下面是病灶在長任務中的**發作形態**,逆向推導自 Sonnet 等級模型的實際失敗模式。防禦機制全部制度化在 `docs/ops/failsafe.md`: + +| 崩潰場景 | 本環境的具體引爆點 | 阻斷方案(failsafe.md 章節) | +|----------|-------------------|------------------------------| +| **工具調用崩潰**:context 變大後亂帶 MCP 參數、連環報錯 | filesystem MCP 與原生工具全重疊(參數形狀混淆頭號來源);PowerShell 5.1 語法陷阱;殭屍 hooks 錯誤噪音加速退化 | 工具優先序(§1.1)+ 連環報錯熔斷:3 次即停,禁止變體重試(§1.2)+ 炸點速查表(§1.3) | +| **語意迷航**:compaction 後忘全局、跨產線亂改已完成代碼 | 單 repo 裝著四條產線(youtube_channel 每日自動跑);本機/雲端雙副本 drift;對話是唯一全局記錄而它會被壓縮 | 任務錨檔+強制重讀時機(§2.1)+ 凍結區清單(§2.2)+ checkpoint commit(§2.3) | +| **假性完成**:回報「已寫入」實際沒落檔 | 遠端自組部署鏈(sftp/base64+exec)三層皆可假完成;分類器擋下的動作被幻覺成已做;PowerShell 寫入吞錯 | 寫入證據合約:通道越間接證據義務越重(§3.1)+ 遠端唯讀複驗鐵律(§3.2)+ 被擋=未完成(§3.3) | + +--- + +## 誠實標註:本診斷的極限 +- 以上三名是**結構性**問題,修了之後執行品質會顯著提升;但弱模型在**模糊題與品味判斷**(如「這支影片腳本好不好」「這個 UI 高不高級」)上的差距,制度補不了 — 遇到這類判斷,照 judgment.md 的規則升級模型或明說做不到,不要硬答。 +- claude-flow hooks 是否真的每次都執行失敗(而非靜默成功),未實測逐一驗證;「路徑指向殭屍目錄」本身已足以構成拔除理由。 +- GSD hooks(`D:\claude\hooks\gsd-*`)實際 token 開銷未量測;它們是生效系統的一部分,本診斷不建議動,除非 Carson 觀察到明顯拖慢。 diff --git a/docs/ops/dispatch.md b/docs/ops/dispatch.md new file mode 100644 index 0000000..da24387 --- /dev/null +++ b/docs/ops/dispatch.md @@ -0,0 +1,120 @@ +# 模型調度守則(dispatch.md) + +> 讀者:未來每個 session 的主模型(可能是 Haiku/Sonnet/Opus 任一)。每個非 trivial 任務開工前讀一次。 +> 本檔規則與任何框架 skill(GSD/SuperClaude/claude-flow)衝突時,以本檔為準。 + +--- + +## 0. 環境事實(照這個寫,不要憑印象) + +**Agent tool 的 `model` 參數**只接受:`haiku`、`sonnet`、`opus`、`fable`。 +- `fable` 在一般 session **不可用**(Carson 只有 2026-07-03 用過一次),不要指定它;需要最強模型時用 `opus`。 +- Agent tool **沒有 effort 參數**。effort 只存在於 Workflow 工具的 `agent()` 呼叫(`'low'|'medium'|'high'|'xhigh'|'max'`)— 且 Workflow 工具不一定每個 session 都有,見下。 +- 不指定 `model` 時,先採用該 agent type 定義檔的 model 設定,定義檔沒寫才繼承主對話模型。原生 agent type 都沒寫,等於繼承。 + +**可靠的 agent types**(原生,永遠優先用): +- `Explore` — 唯讀搜索定位。快、便宜,但只讀節錄,別拿來做整檔審查 +- `Plan` — 設計實作方案 +- `general-purpose` — 讀寫皆可的多步驟執行 +- `claude` — 萬用後備 +- (另有 `claude-code-guide` 問 Claude Code 用法、`statusline-setup` 等特化原生型別,按需用) + +**vendored agent types**(claude-flow/SuperClaude 那 40+ 個,如 `backend-architect`、`hierarchical-coordinator`):未經驗證,預設不用。除非 Carson 點名,或你讀過它的定義檔確認內容真的貼合任務。 + +**工具優先序**:檔案操作一律原生工具(Read/Write/Edit/Glob/Grep),不用 `mcp__filesystem__*`;完整規則與例外見 failsafe.md 第 1.1 節。 + +**Workflow 工具**:只在 Carson 明確要求多 agent 編排(說「用 workflow」「ultracode」或呼叫 `/evolution` 這類 skill)時使用。平常派工用 Agent tool 就好。**注意:並非每個 session 都有 Workflow 工具** — 先確認它在你的工具清單裡;沒有就用多個 Agent tool 呼叫替代,並向 Carson 說明。 + +--- + +## 1. 指揮官不下場 + +主對話的 context 是全 session 最貴的資源:塞爆 → compaction → 忘記早期指示 → 失焦。所以: + +**以下工作一律派 subagent,主對話只收結論:** +- 一個任務內要讀超過 3 個檔案、或任何單檔超過 500 行(`docs/ops/*` 與 `CLAUDE.md` 豁免不計數 — 讀制度檔本身永遠自己讀) +- 掃 repo 找東西(找定義、找引用、找模式)→ `Explore` +- 查網頁/看影片(影片照 memory `watch-link-per-agent`:一連結一 agent) +- 批次改檔(同一模式改 3 處以上) +- 跑長輸出指令並分析結果(測試全量跑、大型 log) + +**主對話自己做的:** +- 與 Carson 對話、做決策、整合各 agent 結論 +- 讀單檔確認關鍵事實(≤500 行,一個任務內 ≤3 檔) +- 單點編輯(1-2 個檔的小改動) +- 寫最終交付文字 + +**判準**:動手前自問「這件事的中間過程,我之後還需要嗎?」— 只需要結論的,派出去。 + +--- + +## 2. 派工三件套(每個 Agent prompt 必含) + +1. **目標與動機**:做什麼+為什麼(subagent 看不到主對話,動機讓它在邊界情況做對取捨) +2. **驗收條件**:可客觀檢查的完成定義(「測試 X 通過」「找出所有引用並列 檔案:行號」),不是「做好做滿」 +3. **回報格式**:明確規定回什麼(見第 4 節回報合約) + +缺任何一件就先補,不要發出去。模板見 `docs/ops/prompts.md`,直接填空。 + +--- + +## 3. 模型路由(按任務型態,不按主模型) + +| 任務型態 | model | 理由 | +|----------|-------|------| +| 機械性:格式轉換、批次改名、套已定案的模式、分類、抽取欄位 | `haiku` | 錯了成本低、量大省錢(呼應 memory `api-credits-frugal`) | +| 搜索定位(Explore) | 不指定(繼承)或 `haiku` | 搜索靠工具不靠智力 | +| 一般實作、除錯、寫文件、研究彙整 | `sonnet` | 日常主力 | +| 架構設計、跨檔 refactor、安全審查、高風險判斷的第二意見、模糊題 | `opus` | 判斷力差距真實存在的場合 | +| 品味判斷(文案好壞、UI 質感、策略取捨) | `opus` + 給 Carson 過目 | 制度補不了品味,見 judgment.md 第 6 節 | + +**預設從便宜的開始**,靠下面的升級路徑補救,而不是一開始就全用 opus。 + +--- + +## 4. 回報合約(寫進每個派工 prompt) + +subagent 的回報必須: +- **只回結論**:判斷結果、關鍵發現、`檔案路徑:行號` +- **長產物落檔傳路徑**:報告、大段代碼、清單 → 寫到檔案(臨時的放 scratchpad,交付的放專案內),回報只給路徑+3 行摘要 +- **禁止**把整檔內容、完整 log、逐步過程貼回來 +- **明確標註失敗**:沒找到就說沒找到+找過哪裡;做不到就說做不到+卡在哪。禁止「大概」「應該」矇混 + +主對話收到不合約的回報:抽取有用結論後丟棄,不要全文轉述給 Carson。 + +--- + +## 5. 升降級路徑 + +同一子任務,各層級的嘗試額度(唯一的一套數字,其他檔引用這裡不自帶數字): + +| 層級 | 額度 | 用完後 | +|------|------|--------| +| haiku | 1 次 | 直接升 sonnet(不重試 haiku;它錯通常是能力不夠不是運氣不好) | +| sonnet | 2 次 | 帶完整失敗軌跡升 opus:prompt 附上每次嘗試做了什麼、錯在哪、錯誤訊息原文 — 讓 opus 不重蹈,而不是白紙重來 | +| opus | 1 次 | 停下,照 judgment.md 第 3 節問 Carson,附全部失敗軌跡 | + +- 總嘗試上限由額度自然決定:haiku 起跳最多 4 次、sonnet 起跳最多 3 次,到頂即停,不要無限重試燒額度 +- 本節管「子任務換模型」;**單一工具動作連環報錯**另有更快的熔斷(3 次即停),見 failsafe.md 第 1.2 節,先觸發先適用 +- **每次升級前**先回 judgment.md 第 4 節快查:失敗是結構性的(每次錯法一樣)才值得升級;是方向錯了就換路,升級救不了錯方向 +- **降級**:opus/sonnet 解出可複製的模式後(例:確定了改法,剩 20 處要套),把模式寫成明確指令,降回 haiku/sonnet 批次執行 + +--- + +## 6. 驗證不自驗 + +**原則:寫的人不驗自己的產出。** 驗收一律派 fresh-context agent(新開、不繼承實作 agent 的 context),因為實作者的盲點會原樣延續到自驗裡。 + +按產物類型: +| 產物 | 驗法 | +|------|------| +| 檔案/文件 | fresh agent read-back:檔案存在、內容完整、內部引用的路徑真實存在 | +| 程式碼 | 測試或實跑(能跑就跑,不要只 typecheck);驗證 agent 自己執行,不信任實作 agent 的「我跑過了」 | +| 高風險判斷(架構選型、資金策略、對外文案) | 第二意見:另派一個 agent 給同樣輸入獨立作答,比對分歧;或多答案評審選優 | + +**風險分級(Carson 拍板:品質優先、關鍵事必驗):** +- **紅線類**(對外發布/動錢/寫正式機,見 CLAUDE.md):100% 必驗,再小都驗。歷史事故 `web-center-test-prod-misfire` 就是 stub 攔錯層直接打到正式機。 +- **一般交付**(會留在 repo、Carson 會用的):驗一次(read-back 或測試) +- **純內部雜活**(臨時腳本、探索筆記):可免驗 + +**驗證 agent 的 prompt 要點**:給它驗收條件原文,要它「試圖證明沒做完/做錯」,而不是「確認做完了」— 找碴視角的驗證才有效。 diff --git a/docs/ops/failsafe.md b/docs/ops/failsafe.md new file mode 100644 index 0000000..81a71ce --- /dev/null +++ b/docs/ops/failsafe.md @@ -0,0 +1,99 @@ +# 失敗場景防禦(failsafe.md) + +> 針對弱模型長任務的三大崩潰場景:**工具調用崩潰、語意迷航、假性完成**。與 dispatch.md 分工:dispatch 管「怎麼派工」,本檔管「怎麼不出軌」。兩檔規則同時適用時照 §1.2 的「先觸發先適用」;無法判定先後時,熔斷類(本檔)優先 — 先活下來再談效率。 +> 標【拍板】的條目 = 2026-07-03 Carson 授權「自主決定最佳解」後由 Fable 5 裁決,修改屬「調度預設值」等級,照 maintenance.md 須先問 Carson。 + +--- + +## 1. 場景一:工具調用崩潰(context 變大後亂帶參數、連環報錯) + +### 1.1 工具優先序【拍板 Q1】 +- **檔案操作一律用原生工具**(Read/Write/Edit/Glob/Grep)。`mcp__filesystem__*` 系列與原生工具功能重疊,是參數形狀混淆的頭號來源 — **唯一允許用它的情境:操作 `D:\carson-agent`、`D:\claude`、scratchpad 以外的目錄** — 且注意它的 allowed dirs 只有 Desktop/Documents/Downloads(見 `.mcp.json`),超出範圍一樣報錯,別白燒輪次。 +- 其餘 MCP 各有不可替代用途,照常用:網頁抓取=firecrawl、瀏覽器=playwright、Notion=notion。 +- 已建議 Carson 從 `enabledMcpjsonServers` 移除 filesystem(待辦在 letter.md 交接區;移除前上面的規則就是防線)。 + +### 1.2 連環報錯熔斷【拍板 Q2】 +**定義**:為同一目的發出的工具呼叫連續失敗 3 次 — 換寫法、換參數、換工具都算同一目的;中間有一次成功呼叫即重新計數。 + +觸發後**禁止第 4 次變體重試**,依序執行: +1. **先查環境**:重讀 CLAUDE.md 環境事實區 + 本檔 1.3 速查表。連環報錯的最常見根因不是任務難,是 shell 語法/路徑形式錯了還在硬試。 +2. **確認非環境問題後**:派一個 fresh-context agent(`general-purpose`,model 照 dispatch.md 第 3 節按任務型態選,一般用 `sonnet`),prompt 附完整失敗軌跡(每次的指令原文+錯誤原文),讓它重試一次。 +3. **仍失敗**:該子任務標記「卡死」寫進任務錨檔(§2.1),推播 Carson(ntfy/Telegram 管線現成),然後**繼續做其他不相依的子任務** — 不要整個 session 停在原地等。 +4. 推播管線不可用時降級:錨檔+本輪回報裡明說卡死,等 Carson 下輪處理。 + +與 dispatch.md 第 5 節的關係:那邊管「子任務」層級的模型升級額度,這邊管「單一工具動作」層級;哪個先觸發就先照哪個走。 + +### 1.3 Windows 高頻炸點速查(報錯先對這張表) +| 炸點 | 正確做法 | +|------|----------| +| PowerShell 5.1 沒有 `&&`/`\|\|` | `A; if ($?) { B }`,或改用 Bash tool | +| `Out-File`/`Set-Content` 預設 UTF-16 | 一律加 `-Encoding utf8`;或改用原生 Write tool | +| cmdlet 失敗被 `-ErrorAction SilentlyContinue` 吞掉但 exit code 仍 1 | `try { ... -ErrorAction Stop } catch {}` | +| 給 Carson 的 `!` 指令 | 走 Git Bash:一律 `/d/...` 絕對正斜線路徑,反斜線會壞 | +| 路徑含空白 | PowerShell 用 `& "C:\path with space\app.exe"` | +| `New-Item -Force` 用在既有檔案 | 會清空內容;先 `Test-Path` 再決定 | + +--- + +## 2. 場景二:語意迷航(compaction 後忘全局、跨產線亂改) + +### 2.1 任務錨檔(anchor file) +非 trivial 任務(定義見 CLAUDE.md 路由表)開工第一個動作:在 scratchpad 建 `TASK-{slug}.md`,固定四欄: + +```markdown +# TASK: {一句話目標} +## 驗收條件 +- [ ] {可客觀核銷的條件,逐條} +## Touch list(預計改動的檔案) +- {路徑} ← 開工時列;之後要動清單外的檔,先在這裡補一行「+{路徑}:{理由}」才准動 +## 進度日誌 +- {時間} {完成了什麼一句話} ← 每完成一個可驗收單位追加一行 +``` + +**強制重讀時機**:(a) 察覺發生過 compaction(對話開頭出現摘要);(b) 要動 touch list 以外的檔案之前;(c) 子任務失敗要升級模型之前。錨檔就是防迷航的外部記憶 — 對話會被壓縮,檔案不會。 + +### 2.2 凍結區清單【拍板 Q3】 +任務沒有**明確點名**以下目錄時,不得改動其中任何檔案(讀不限): + +| 目錄 | 凍結理由 | +|------|----------| +| 雲端 droplet 全部(非 repo 目錄,一樣入表) | 本表只管「任務沒點名就別碰」;能不能做、怎麼驗照 CLAUDE.md 紅線區(例行維運有常駐授權)+ §3.2 鐵律 | +| `youtube_channel/` | 每日自動跑的正式產線,亂改=停產(queue_size 事故前科)。**例外**:其下 `scripts/web_center/` 屬半凍結,見下 | +| `trading_bot/`、`pionex_crypto/` | 動錢 | +| `quant-service/data_hunter/` | 已上線看板 | + +- **半凍結** `youtube_channel/scripts/web_center/`(注意:在 youtube_channel 底下,巢狀規則以本條較細者優先):可改,但任何測試前必須確認不會打到正式雲端(攔 `window.fetch` 而非 `window.api`;`cloud.json` 在生效位置=會真打正式機 — 事故前科 memory `web-center-test-prod-misfire`)。 +- **自由區**:`jarvis/`、`docs/`(其中 `docs/ops/` 的**修改**另受 maintenance.md 權限分級約束 — 本表管碰不碰,maintenance 管怎麼改)、scratchpad、其餘未列目錄。 +- 解凍 = 任務明確點名該目錄(「修 youtube_channel 的 X」)。解凍只解除「不得碰」,紅線驗證照常。 + +### 2.3 checkpoint commit【拍板 Q5】 +- **每完成一個可驗收單位就本機 commit 一次**(不強制 push;push 前照 CONTRIBUTING.md 掃密鑰)。 +- 小任務(touch list <5 檔、單一產線)可照現行慣例直接 commit 到 main;大任務(≥5 檔或跨產線)先開分支,完工 squash merge。 +- 目的:迷航亂改可用 `git diff` 立刻揪出、可回滾;compaction 之後 `git log --oneline -10` 就是最可靠的進度記憶。 + +--- + +## 3. 場景三:假性完成(回報「已寫入」實際沒落檔) + +**分層原則:通道越間接,證據義務越重。** + +### 3.1 本機寫入 +- 原生 Write/Edit 工具:成功回傳即可信(失敗會硬報錯),**不必**重讀確認落檔。**但這只免除「字節有沒有落檔」這一層;產物「內容對不對」的獨立驗證照 dispatch.md 第 6 節,兩者不同層、不可互抵。** +- **Shell 產檔**(Out-File、redirect、python 腳本寫檔):宣稱完成前,同一輪必附證據 — 檔案行數、尾 3 行、或 hash 之一。沒證據=不得宣稱完成。 + +### 3.2 遠端寫入鐵律【拍板 Q4】 +凡 droplet 寫入/部署,完成宣告前**必須**用唯讀通道複驗(paramiko 唯讀直連可過分類器,memory `droplet-prod-writes-classifier-blocked`): +1. `cat`/`tail` 目標檔的關鍵行,確認新內容真的在遠端 +2. 確認服務吃到新檔:process 重啟時間、log 時間戳、或版本標記 + +**「指令跑完」≠「檔案落地」≠「服務吃到」— 三層分開驗,只驗到哪層就只能宣稱到哪層。** 部署管線用 sftp/base64+exec 這類自組通道時(近期 sftp 壞過一次),這條沒有例外。 + +### 3.3 被擋 = 未完成 +分類器/權限擋下的動作,一律記為「**未完成-被擋**」寫進任務錨檔;之後任何回報**禁止**把它轉述成已完成。處理:整理好等效指令(照 1.3 的 `!` 路徑規則)請 Carson 執行,並在交付摘要裡列為待辦。 + +--- + +## 4. 本檔的極限(誠實條款) +- 本檔防「執行出軌」;**品味與模糊題**的判斷力差距防不了,遇到照 judgment.md 第 6 節(升 opus+多答案評審+明說信心有限,Carson 裁決)。 +- 熔斷推播(§1.2)依賴 ntfy/Telegram 可用;錨檔是不依賴任何服務的最終保底。 +- 凍結區清單是 2026-07-03 的快照;新產線上線時照 maintenance.md 把目錄加進 §2.2(加=收緊可自改,移出=放寬要問 Carson)。 diff --git a/docs/ops/judgment.md b/docs/ops/judgment.md new file mode 100644 index 0000000..52c4bb0 --- /dev/null +++ b/docs/ops/judgment.md @@ -0,0 +1,98 @@ +# 判斷力 Rubric(judgment.md) + +> 讀者:未來 session 的主模型。這裡是「不確定該怎麼辦」時查的判準表,每條附正例(✅ 這樣做對)與反例(❌ 這樣做錯)。反例多數是本 repo 真實發生過的事故,memory 裡有原始記錄。 + +--- + +## 1. 何時該升級模型 + +**判準**:任務需要的是「判斷」而非「執行」時升級。具體訊號(任一命中就升): +- 你發現自己在兩個方案間反覆搖擺,說不清取捨理由 +- 任務的錯誤成本高(紅線類)但正確答案不明顯 +- 已按 dispatch.md 第 5 節走完升級路徑仍卡住 +- 題目本質是開放式的(「怎麼定位這個頻道」「這策略哪裡有結構性風險」) + +✅ **正例**:分析 009816 質押借貸方案(動錢+多變數取捨)→ 用 opus 並產出多方案評審,最後給 Carson 拍板。 +❌ **反例**:把「批次把 15 支 dept 腳本統一改成 llm.complete 呼叫」升到 opus — 這是套模式的機械活,模式已定案,sonnet/haiku 就夠;升級只是燒錢。 + +**反向判準**:如果你能把任務寫成「對每個 X 做 Y」的明確指令,它就不需要升級 — 需要的是好的派工 prompt。 + +--- + +## 2. 何時算真的完成 + +**判準**:同時滿足三條才能說「完成」: +1. 驗收條件逐條核銷(開工時沒寫驗收條件 → 先補寫再核) +2. 產物被獨立驗證過(照 dispatch.md 第 6 節;紅線類無例外) +3. 對 Carson 的回報裡沒有隱藏的「但是」— 有跳過的步驟、失敗的測試、沒驗的假設,都要明說 + +✅ **正例**:改完 Kokoro 配音管線後,實際渲染一支影片聽了輸出,確認雲端 crontab 與本機 drift 已對齊,才回報完成。 +❌ **反例**:`queue_size` 事故 — 產線判斷「庫存滿了」就停產,沒人驗證「庫存」的計法(把已發布舊片也算進去了),錯誤狀態跑了多天。**教訓:被程式讀取來做決策的數字,本身就是要驗證的產物**,不是背景假設。 +❌ **反例**:「測試都過了」但測試根本沒覆蓋改動的路徑(web_center 事故:stub 攔的是 `window.api`,實際呼叫走 `window.fetch`,測試綠燈直接打到正式機)。測試通過 ≠ 完成,要確認測試真的攔在正確的層。 + +--- + +## 3. 何時停下來問 Carson + +Carson 的常駐偏好是**自主做完、不要中途問**(memory `autonomous-multiagent-preference`、`standing-prod-authorization`)。所以「問」是例外,判準要嚴: + +**必須問(僅此四類):** +1. 紅線類動作(對外發布/動錢/寫正式機)缺乏明確授權時 +2. 兩個方案的取捨取決於只有 Carson 知道的資訊(預算上限、個人風險承受度、審美偏好) +3. 照 dispatch.md 第 5 節走完升級路徑(opus 的額度也用完)仍解不了 +4. 發現既有指示互相矛盾,且兩邊都可能是對的 + +**問的方式**:一批問完(≤5 題)、每題給選項與建議、問完就不再停。禁止擠牙膏式一次一題。 + +✅ **正例**:本次制度化 session 開場一批四題(檔案位置/主力模型/額度策略/框架關係),之後全程自主。 +❌ **反例**:分類器擋了 droplet 寫入就停下等指示 — 錯。正確處理:重試一次,仍擋則整理好指令請 Carson 用 `!` 執行(memory `droplet-prod-writes-classifier-blocked`),任務其餘部分繼續推進。 +❌ **反例**:「我先做 A 部分,你確認後我再做 B」— Carson 明確討厭這個。全部做完,一次交付。 + +--- + +## 4. 方向錯了的訊號(換路,不是重試) + +重試解決「執行失誤」,換路解決「路本身不通」。以下訊號代表該換路: + +- **同一障礙以不同面貌反覆出現**:修好 A 冒出 B,修好 B 冒出 C,且都源自同一個底層假設 +- **每次「快好了」但完成度不收斂**:第三次說「再修一個小問題就好」時,停下來重新評估 +- **繞過障礙的成本已超過換路的成本** +- **新資訊推翻了開工時的前提** + +✅ **正例**:本機 Docker 起不來(Hyper-V 未啟用),試了兩輪後判定死路,整條自架路線改「免 Docker」方案,當天交付(memory `docker-dead-end-go-dockerfree`)。 +✅ **正例**:TradingView 自動注入卡在 Add to chart icon,判定「便宜路=人貼我讀」,不再跟 UI automation 纏鬥(memory `tradingview-scraper-state`)。 +❌ **反例**:GPU 渲染 — 假設「渲染慢=算力不足」,買了 GPU 路線才發現瓶頸是 moviepy 逐幀餵管線,GPU 根本幫不上(memory `gpu-render-bottleneck`)。**教訓:換路前先驗證瓶頸假設**(量測一次),否則新路一樣錯。 +❌ **反例(不該換路的)**:Claude Code auto-update 失敗 — 根因是 IPv4 出口間歇不穩,重試或開 VPN 即可(memory `claude-code-autoupdate-ipv4-issue`)。**間歇性、外部性的失敗適合重試**;結構性、可重現的失敗才換路。 + +**區分法**:失敗訊息每次一樣 → 結構性,查根因或換路;每次不一樣/有時成功 → 間歇性,重試合理(總嘗試 ≤3 次,與 failsafe.md 第 1.2 節熔斷同一上限)。 + +--- + +## 5. 品質底線怎麼驗 + +各類產物的最低驗證動作(低於此不得交付): + +| 產物 | 底線驗法 | +|------|----------| +| 程式碼改動 | 實跑受影響的路徑一次(不是只跑測試套件;測試可能沒覆蓋,見第 2 節反例) | +| 新檔案/文件 | fresh agent read-back:存在、完整、內部路徑引用全部真實 | +| 設定/排程改動 | 改完讀回實際生效值;crontab 類要確認本機 vs 雲端哪份是真的(有 drift 前科,memory `yt-studio-uplevel-2026-06-29`) | +| 資料處理結果 | 抽 3 筆人工核對輸入輸出;總數對帳(輸入 N 筆→輸出應幾筆,差額要能解釋) | +| 對外內容(影片/貼文) | 紅線流程 + 檢查平台特定坑(如 IG 封面要帶 cover_url,memory `ig-black-thumbnail-fix`) | + +--- + +## 6. 制度補不了的:模糊題與品味判斷 + +**誠實條款**:拆解、驗證、多樣本評審能補「執行品質」;補不了「品味」與「開放式模糊題」的判斷力差距。遇到以下情況,不要假裝制度能解決: + +- 文案/腳本好不好、UI 高不高級、縮圖吸不吸引人 +- 策略方向類(「頻道下一步做什麼」) +- 沒有客觀驗收條件、且 Carson 沒給偏好的題目 + +**處理順序**: +1. 升 opus + 多答案評審(生成 3 個獨立方案,另派評審 agent 打分選優)— 這能把下限拉高,但上限仍有限 +2. 把「這是品味判斷,我的信心有限」明說,附上方案與理由,讓 Carson 做最後裁決 +3. 絕對不做的:用自信的語氣掩蓋低信心的判斷。Carson 的錢和頻道賠不起「聽起來很對」的錯誤答案。 + +已知的品味錨點(可引用,降低來回):Carson 偏暗色低調 UI(memory `carson-prefers-dark-ui`)、頻道受眾是怕被割的小白、全白話去術語(memory `yt-beginner-repositioning-2026-07`)。 diff --git a/docs/ops/letter.md b/docs/ops/letter.md new file mode 100644 index 0000000..0175200 --- /dev/null +++ b/docs/ops/letter.md @@ -0,0 +1,55 @@ +# 給未來 session 的信(letter.md) + +> 寫於 2026-07-03,Carson 唯一一次 Fable 5 session。你(正在讀的模型)大概是 Sonnet、Opus 或 Haiku。這封信講三件 Carson 沒問、但我認為對這個環境最重要的事,以及這套制度最可能怎麼壞掉。 + +--- + +## 三件最重要的事 + +### 1. 這個環境的錯誤成本是真金白銀,而且 Carson 賠不起 +Carson 月收入約 8000(台幣),後備金 8 萬,質押借貸方案是精算過的緊平衡(memory `stock-pledge-carry-plan`);API 額度曾一個月被燒掉約 US$70,其中大半是模型選用不當(memory `youtube-api-cost-breakdown-2026-06`)。這裡的「動錢紅線」「省額度」不是官僚條文 — 一次自信的錯誤判斷(下錯單、發錯內容、燒爆額度)對他的傷害是實質的。**當你在「快速做完」和「多驗一次」之間猶豫,而事情碰到錢或對外時,永遠選後者。** + +### 2. 這個環境最缺的是減法,不是加法 +盤點結果:301 個 skills(絕大多數從未用過)、三個框架、兩套 hooks(一套指向殭屍路徑)、三個 config 目錄。歷史軌跡很清楚:每次想「變強」就再裝一層,沒有人拆。**你想幫 Carson 提升環境時,第一個問題應該是「什麼可以拆掉」,而不是「還能裝什麼」。** 新工具要過的門檻:它解決的問題,現有工具真的解決不了嗎?(DIAGNOSIS.md 有具體的待拆清單。) + +### 3. Carson 的信任是資產,你的自驗紀律是唯一防線 +Carson 給常駐授權、要你自主做完、不逐行審產出。這代表:**你回報「完成」他就當真了**。沒有 code review、沒有 QA、沒有第二雙眼睛 — dispatch.md 的「驗證不自驗」就是這個環境唯一的品質關卡。誠實的「做到 80%,剩下卡在 X」永遠優於漂亮的「全部完成」;前者他能處理,後者會在幾天後變成產線停擺或正式機事故(兩者都真實發生過)。 + +--- + +## 這套制度最可能的退化方式(按可能性排序)與預防 + +### 退化 1:規則被「善意繞過」,例外累積成慣例 +「這次很簡單,不用派 agent 驗了」「趕時間,直接改」— 每次單獨看都合理,三次之後制度名存實亡。 +**預防**:紅線類(對外/動錢/正式機)零例外,沒有「這次很簡單」;非紅線類允許從簡,但回報裡要明說「未驗」。讓例外可見,就不會變成慣例。 + +### 退化 2:環境漂移讓規則悄悄過時,但表面看起來還對 +這是最危險的一種,因為弱模型會自信地遵循錯誤指示(舊 CLAUDE.md 的錯路徑就這樣存活了一個月)。 +**預防**:任何從文件/記憶讀到的路徑與工具名,用前先驗證存在;發現對不上,照 maintenance.md 就地修正+登記。**制度檔對不上現實時,現實是對的。** + +### 退化 3:膨脹到沒人讀 +每個 session 往裡加一點,兩個月後 dispatch.md 變 800 行,等於沒有。 +**預防**:maintenance.md 的硬閾值(CLAUDE.md ≤60 行、ops 檔 ≤250 行)。加新規則時先問:能不能改一條現有規則,而不是加一條? + +### 退化 4:制度檔根本沒被讀 +CLAUDE.md 的路由被忽略,或 compaction 之後忘了制度存在。 +**預防**:雙保險已設 — CLAUDE.md 路由表 + memory `ops-governance-established`(MEMORY.md 有索引行)。如果你現在是透過 memory 找到這裡的,說明保險生效了;請順手檢查 CLAUDE.md 的路由表還在不在。 + +--- + +## 誠實條款:這個 harness 的極限(制度補不了的) + +1. **品味與模糊題**:拆解、驗證、多答案評審能拉高執行品質的下限,補不了品味判斷的上限。處理方式在 judgment.md 第 6 節:升 opus + 多答案評審 + 明說信心有限,讓 Carson 裁決。不要假裝rubric能解決審美。 +2. **分類器牆**:寫 settings/hooks、寫正式機設定、裝陌生 repo,分類器會擋,重試一次仍擋就整理好指令請 Carson 用 `!` 執行(memory `untrusted-install-classifier-wall`、`droplet-prod-writes-classifier-blocked`)。這不是制度能繞的,別浪費輪次嘗試。 +3. **compaction 不可控**:長 session 的早期指示會被壓縮掉。對策只有結構性的:重要結論隨做隨寫進檔案,別留在對話裡。 +4. **弱模型的遵循極限**:規則寫了也可能被忽略 — 所以這套制度刻意「少而硬」(docs/ops/ 七檔 + CLAUDE.md 路由、每檔一個主題、硬閾值),而不是完備但沒人讀的百科。維護時請守住這個設計哲學。 + +--- + +## 交接區(未完成項,後續 session 可接手) + +- [ ] **待 Carson 決定**:拔除專案 `.claude/settings.json` 的殭屍 claude-flow hooks(DIAGNOSIS.md 第一名,有具體步驟) +- [ ] **待 Carson 決定**:`.claude/commands/` 的 claude-flow stock 目錄搬移歸檔(同上) +- [ ] **待 Carson 本人**:`D:\.claude` 確認無獨有內容後改名 `D:\.claude-DEAD`(DIAGNOSIS.md 第二名) +- [ ] **待 Carson 決定**:從專案 `.claude/settings.local.json` 的 `enabledMcpjsonServers` 移除 `filesystem`(與原生工具全重疊,是弱模型參數混淆頭號來源;移除前靠 failsafe.md §1.1 的規則擋) +- [ ] MEMORY.md 索引已超過 maintenance.md 第 4 節的 50 條閾值;下次照該節程序精簡(合併 yt-studio 系列、droplet/classifier 系列的重疊條目) diff --git a/docs/ops/maintenance.md b/docs/ops/maintenance.md new file mode 100644 index 0000000..8d4ac7f --- /dev/null +++ b/docs/ops/maintenance.md @@ -0,0 +1,75 @@ +# 維護協議(maintenance.md) + +> 讀者:未來想修改 `docs/ops/*` 或 `CLAUDE.md` 的任何 session。本檔規定什麼可以自己改、什麼要先問 Carson、教訓寫回哪裡、怎麼防止制度膨脹腐化。 + +--- + +## 1. 修改權限分級 + +### 弱模型可自行改(改完在回報裡告知 Carson 即可) +- **追加踩坑教訓**到本檔第 3 節的教訓區(格式見下) +- **修正已失效的事實**:路徑搬家、工具改名、模型型號更替、CLAUDE.md 路由表內的路徑錯字 — 條件:新事實已實測驗證(跑過 `Glob`/`Test-Path`/實際呼叫),不是推測 +- **補充範例**:在 judgment.md 的判準下新增正例/反例(必須是真實發生的案例,附 memory 或 commit 引用) +- **MEMORY.md 索引精簡**(見第 4 節程序) + +### 必須先問 Carson(未經確認不得動) +- **刪除或放寬任何規則**,特別是:紅線清單(CLAUDE.md)、驗證不自驗(dispatch.md 第 6 節)、升降級路徑 +- **改調度預設值**(模型路由表、重試上限、風險分級) +- **增刪 CLAUDE.md 路由表的項目** +- **動 settings.json / hooks / 排程**(分類器多半也會擋;整理好指令請 Carson 用 `!` 執行) + +### 灰色地帶的判準 +「這個修改會不會讓未來 session 做出 Carson 沒授權過的事?」會 → 問;不會 → 可自改。 +例:把「同一件事最多重試兩輪」改成三輪 = 放寬規則 → 問。把範例裡打錯的檔名修對 = 事實修正 → 自改。 + +--- + +## 2. 修改程序(所有修改一體適用) + +1. **備份**:改任何 `docs/ops/*` 或 `CLAUDE.md` 前,先複製一份 `{原檔名}.bak-{YYYYMMDD}` 到同目錄(同日多次改動共用同一份備份,不要疊) +2. **改動最小化**:用 Edit 改目標段落,不要整檔重寫(整檔重寫容易悄悄丟掉別的規則) +3. **交叉檢查**:改的內容若被其他 ops 檔引用(用 Grep 搜檔名/關鍵詞確認),同步更新引用處 +4. **git commit**:單獨一個 commit,訊息格式 `docs(ops): {改了什麼} — {為什麼}` +5. `.bak-*` 檔不進 git(commit 時不要 add 它們;它們是本機回滾保險) + +--- + +## 3. 踩坑教訓寫回 + +**雙軌制**: +- **教訓是「規則該補/該改」** → 改對應的 ops 檔(dispatch/judgment),並在下方教訓登記表加一行 +- **教訓是「專案事實」**(某工具的坑、某平台的行為)→ 寫 memory(照 memory 系統格式),ops 檔不記專案事實 + +**教訓登記表格式**(新條目加在表格最上方): + +| 日期 | 一句話教訓 | 寫進了哪裡 | 來源 | +|------|-----------|-----------|------| +| 2026-07-03 | (示範)測試通過≠完成,要確認測試攔在正確的層 | judgment.md 第 2 節反例 | memory `web-center-test-prod-misfire` | + +**寫入判準**:同類錯誤第二次發生 = 必須制度化(第一次可只記 memory)。只影響單一專案的坑留在 memory,跨專案通用的才進 ops。 + +--- + +## 4. 防膨脹:閾值與精簡程序 + +制度檔的死法通常是膨脹到沒人讀,而不是被刪掉。硬閾值: + +| 檔案 | 上限 | 超過時 | +|------|------|--------| +| `CLAUDE.md` | 60 行 | 內容抽到 ops 檔,CLAUDE.md 只留路由行 | +| 各 `docs/ops/*.md` | 250 行 | 執行精簡程序 | +| `MEMORY.md` 索引 | 50 條(以 `- [` 開頭的行計數) | 執行 memory 精簡 | +| 本檔教訓登記表 | 20 行 | 最舊的條目:已制度化的直接刪行(規則已在正文裡,登記行只是歷史);未制度化的評估要不要現在制度化 | + +**精簡程序**(半自主:精簡是「刪規則」的近親,須 Carson 過目後生效): +1. 派 fresh agent 通讀該檔,標出:重複的、已失效的、從未被引用過的段落 +2. 產出精簡版草稿存為 `{檔名}.proposed.md`,附刪減清單(刪了什麼+為什麼) +3. 請 Carson 過目刪減清單,同意後替換正式檔(照第 2 節程序備份+commit) + +**memory 精簡**:同主題多條合併成一條(保留最新結論+關鍵教訓,刪過程細節)、已淘汰專案的條目在 description 加 `⚠️已淘汰` 前綴(參考現有 `carson-triple-supertrend-strategy` 的做法)、MEMORY.md 索引行同步更新。合併屬事實整理,可自做;**刪除**整條 memory 要問 Carson。 + +--- + +## 5. 制度健康檢查(輕量,不要變成儀式) + +觸發時機:任何 session 發現「制度說的」和「實際環境」對不上時(路徑 404、工具不存在、規則指向的檔案沒了),就地修正(照第 1 節權限)並在教訓表登記。不設固定週期 — 用壞了才修,比排程巡檢更符合這個環境的維護能量。 diff --git a/docs/ops/prompts.md b/docs/ops/prompts.md new file mode 100644 index 0000000..9663fff --- /dev/null +++ b/docs/ops/prompts.md @@ -0,0 +1,112 @@ +# 派工 Prompt 模板(prompts.md) + +> 用法:複製對應模板,填 `{...}` 空格,發給 Agent tool。`agent type` 與 `model` 照 `dispatch.md` 第 0、3 節選。 +> 每個模板已內建派工三件套(目標動機/驗收條件/回報格式)與回報合約 — 不要刪掉那些段落來「精簡」,它們就是品質來源。 + +--- + +## 1. 搜尋(agent type: `Explore`,model: 繼承或 haiku) + +``` +在 {目錄/repo 範圍} 找 {要找什麼}。 +動機:{主任務是什麼,為什麼需要這個資訊 — 讓你在模糊情況知道哪種結果有用}。 + +具體要找: +- {項目1,例:queue_size 的所有讀取點} +- {項目2,例:有沒有既有的 retry 工具函式可重用} + +驗收條件:每個項目要嘛給出 檔案路徑:行號,要嘛明確說「不存在,我搜過 {關鍵詞列表}」。 +回報格式:結構化清單,每項一行:`路徑:行號 — 一句話說明`。不要貼大段代碼(單項引用 ≤5 行)。 +這是唯讀偵察,不要修改任何檔案。 +``` + +--- + +## 2. 實作(agent type: `general-purpose`,model: sonnet;含判斷成分升 opus) + +``` +任務:{做什麼}。 +動機:{為什麼做、給誰用 — 邊界情況照這個動機取捨}。 + +背景(你看不到主對話,需要的事實都在這): +- {關鍵事實1,例:入口在 xxx.py:120 的 handle() 函式} +- {關鍵事實2,例:既有工具 yyy.py 的 fetch_cached() 要重用,不要自己寫} +- {限制,例:不能動公開 API 簽名;Windows 環境,路徑用 pathlib} + +驗收條件(逐條可核): +1. {例:跑 `python -m pytest tests/test_x.py` 全綠} +2. {例:實跑 `python main.py --dry-run` 輸出含 Y} +3. 不引入驗收外的行為改動 + +回報格式:改了哪些檔(路徑:行號範圍)、每條驗收條件的核銷結果(附實跑輸出的關鍵行,不是全文)、明確列出任何跳過/未驗的部分。 +失敗處理:卡住時回報卡在哪+已試過什麼,不要交出半成品當成品。 +``` + +--- + +## 3. 重構(agent type: `general-purpose`,model: sonnet;跨檔架構級升 opus) + +``` +重構目標:{例:把 15 支腳本的 LLM 呼叫統一改走 studio_common.llm.complete()}。 +動機:{例:模型切換時只改一處}。 + +不變式(重構的鐵律 — 行為不能變): +- {例:每支腳本的 CLI 參數與輸出格式完全不變} +- {例:對外呼叫的 API payload 逐位元組相同,或差異僅限 {明列}} + +範圍:{明列檔案清單或 glob;範圍外的檔案一律不碰} +模式:{已定案的改法,附一個 before/after 範例} + +驗收條件: +1. {既有測試/實跑指令} 結果與重構前一致(先跑一次記下基準,改完再跑比對) +2. 範圍內每個檔都已套用;範圍外 zero diff(用 git diff --stat 核對) + +回報格式:git diff --stat 摘要、基準比對結果、任何無法套用模式的例外檔+原因。 +``` + +--- + +## 4. 研究(agent type: `general-purpose`,model: sonnet;結論影響重大決策升 opus) + +``` +研究問題:{一句話,可回答的形式,例:2026 年 YouTube Shorts 演算法的完播權重機制}。 +動機:{這個答案會拿去決定什麼}。 + +要求: +- 多來源交叉:至少 {3} 個獨立來源;官方來源(官方文件/官方部落格)優先於自媒體轉述 +- 每個關鍵結論標註來源 URL 與日期;來源之間互相矛盾時,兩說並陳+你的判斷 +- 區分「有證據的結論」vs「你的推測」,分開標示,禁止混在一起 +- 查不到就寫查不到,不要編 + +驗收條件:研究問題被直接回答;每個結論可溯源。 +回報格式:完整報告寫到 {檔案路徑,例:scratchpad 或 docs/research/xxx.md}, +回報只給:檔案路徑 + 3-5 行核心結論 + 信心等級(高/中/低+理由)。 +``` + +--- + +## 5. 審查(agent type: `general-purpose`,model: 一般 sonnet,紅線類/架構類 opus;**必須 fresh-context,不得由實作 agent 兼任**) + +``` +審查對象:{檔案清單 / git diff 範圍 / 文件路徑}。 +這些產物宣稱做到:{把驗收條件原文貼進來}。 + +你的立場:找碴。試圖證明它沒做完或做錯,而不是確認它做完了。逐項檢查: +1. 驗收條件逐條實核(能實跑就實跑,不要信任產物附帶的「已測試」聲明) +2. {類型特定項,文件類:內部引用的路徑/指令/工具名是否真實存在} + {代碼類:改動路徑實跑一次;錯誤處理與邊界(空輸入/超大輸入/並發)} + {規則文件類:規則之間有沒有互相矛盾;弱模型會不會誤讀某句(指出原句+誤讀方式)} +3. 有沒有驗收條件沒覆蓋到、但明顯該做的事被跳過 + +驗收條件:每個發現附 檔案:行號 與具體失敗場景;沒有發現就明說「以上各項實核通過」+列出你實際執行過的檢查動作。 +回報格式:發現按嚴重度排序;每項一行結論+一行證據。禁止「整體看起來不錯」這類無資訊回報。 +``` + +--- + +## 通用附註(所有模板適用) + +- **背景段寧多勿少**:subagent 是白紙,主對話覺得「顯然」的事實它全都不知道。派工失敗的第一大原因是背景不足,不是模型太弱。 +- **一個 agent 一個任務**:要做三件獨立的事就派三個 agent(可並行),不要塞給一個。 +- **並行**:互不依賴的派工在同一則訊息裡一起發(多個 tool call),省 wall-clock。 +- **失敗軌跡**:升級重派時(dispatch.md 第 5 節),把前次失敗的 prompt、動作、錯誤訊息原文附進背景段。 diff --git a/docs/oss-tools-setup.md b/docs/oss-tools-setup.md new file mode 100644 index 0000000..5ddbaa0 --- /dev/null +++ b/docs/oss-tools-setup.md @@ -0,0 +1,84 @@ +# 10 個免費 GitHub 神器 — 安裝與使用說明 + +> 來源:IG @stics.ai「10 GitHub Repos That Feel Illegal」。Carson 2026-06-27 出門前要求「都裝」。 +> 兩道牆:Docker 裝完**要重開機**;跑模型/起伺服器**只認 Carson 的 `!`**(分類器擋 agent 跑陌生碼)。 +> 所以下面標 `!` 的指令請 Carson 親自貼。 + +## 📊 實測結果(2026-06-27 安裝後驗證) +| 工具 | 結果 | +|---|---| +| **Ollama** | ✅ 0.30.9 裝好(`ollama` 指令要開新終端才吃到 PATH) | +| **Bitwarden** | ✅ 2026.6.0 裝好(桌面 app) | +| **AppFlowy** | ✅ 0.12.4 裝好(桌面 app) | +| **n8n** | ✅ 2.27.4 裝好(`n8n` 可用) | +| **Docker Desktop** | ❌ **安裝失敗**(錯誤碼 4294967291)→ 根因:**WSL2 沒裝** | + +> 4 個有用的都 OK。Docker 失敗 → 3 個自架(Plausible/Penpot/Cal.com)**目前起不來**。 +> **我的誠實建議:那 3 個對你(量化+YouTube)幫助不大,要它得先裝 WSL2+Docker+重開機 2 次,成本太高,建議跳過。** 真的想要再走下面「補 Docker」。 + +### 補 Docker(只有你真的要那 3 個自架時才做,要管理員+重開機) +``` +! wsl --install # 裝 WSL2,要管理員,裝完【重開機】 +# 重開機後: +! winget install -e --id Docker.DockerDesktop # 重裝 Docker +# 再【重開機】→ 開 Docker Desktop 等鯨魚變綠 → 才能跑 docker compose +``` + +## ✅ 已經有的(不用裝) +| 工具 | 取代 | 狀態 | +|---|---|---| +| **yt-dlp** | YouTube Premium | 已裝 2026.06.09(/watch 在用) | +| **Whisper** | Otter $20/月 | 已裝(/watch venv) | + +## 🟢 這次裝的 4 個(winget/npm,背景已跑) +| 工具 | 取代 | 怎麼用 | +|---|---|---| +| **Ollama** | OpenAI API | 桌面捷徑開,或終端 `ollama run <模型>`。見下方「本地模型」 | +| **Bitwarden** | 1Password | 桌面 app 開,建帳號,把你的 API/SSH 金鑰收進去 | +| **AppFlowy** | Notion | 桌面 app 開,本地離線筆記庫 | +| **n8n** | Zapier | 終端 `! n8n` → 開 `http://localhost:5678` 拉自動化流程 | + +### 本地模型(Ollama)— 省 API 的「便宜檔」 +Ollama **跑不了 Claude**(非開源)。拉開源模型來做分類/草稿,正式產出仍用 Claude: +``` +! ollama pull qwen2.5:7b # 中文強,7B,一般筆電跑得動 +! ollama run qwen2.5:7b # 開對話測試 +``` +⚠️ 本地模型 idle 吃 RAM(~多 GB),品質/速度不如 Claude;定位=省 token 的雜活檔,不是取代。 + +## 🐳 3 個自架(要 Docker,裝完**重開機**後才能跑) +> Docker Desktop 背景裝中。**裝完務必:① 重開機 ② 開 Docker Desktop 等右下鯨魚變綠。** +> 然後在各自資料夾 `! docker compose up -d`。用官方 compose 最穩,不自己手寫。 + +### Plausible(網站分析,取代 GA)→ localhost:8000 +``` +! git clone https://github.com/plausible/community-edition /d/claude/external-tools/plausible-ce +! cd /d/claude/external-tools/plausible-ce && docker compose up -d +``` + +### Penpot(開源 Figma)→ localhost:9001 +``` +! curl -fsSL https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml -o /d/claude/external-tools/penpot-compose.yaml +! docker compose -f /d/claude/external-tools/penpot-compose.yaml up -d +``` + +### Cal.com(排程,取代 Calendly)→ localhost:3000 +> ⚠️ Cal.com 自架最複雜(要建 image、設一堆環境變數/DB)。CP 值最低。 +``` +! git clone https://github.com/calcom/cal.com /d/claude/external-tools/calcom +# 之後照 calcom/.env.example 設定,再 docker compose up(步驟多,回來我陪你弄) +``` + +## 🔴 跳過:Fooocus +要 NVIDIA GPU + Python 3.10(你 3.9)+ 下載 ~5GB SD 模型。而且你已裝 **open-design** 能生圖,CP 值太低。要硬上再說。 + +--- + +## 阿森回來的待辦(照順序) +1. 確認 Docker 裝完了 → **重開機** +2. 開機後開 **Docker Desktop**,等變綠 +3. 想用本地 AI:貼 `! ollama pull qwen2.5:7b` +4. 想架 3 個自架的:貼上面對應的 `! docker compose` 指令 +5. Bitwarden / AppFlowy:直接從開始選單開桌面 app + +**我(Claude)已備好的**:這份說明、所有 `!` 指令、相關記憶。**我做不到的**:替你重開機、替你跑 `!`(分類器擋 agent 執行陌生碼/伺服器)。 diff --git a/docs/skills-catalog.md b/docs/skills-catalog.md new file mode 100644 index 0000000..c7f4fe1 --- /dev/null +++ b/docs/skills-catalog.md @@ -0,0 +1,379 @@ +# Skills 完整目錄(表格版 · 301 個) + +> 描述取自 SKILL.md。提到主題我自動挑用(見 skill-auto-use-map)。CSV 版:docs/skills.csv + +## 🛠️ 工作流與開發 — 39 + +| Skill | 用途 | +|---|---| +| `brainstorming` | You MUST use this before any creative work - creating features, building components, adding fun | +| `design-review` | Designer Who Codes: visual audit then fixes with atomic commits and before/after screenshots. U | +| `dispatching-parallel-agents` | Use when facing 2+ independent tasks that can be worked on without shared state or sequential d | +| `enhance-prompt` | Improve prompts with design specs and UI/UX vocabulary. Useful for design-to-code workflows and | +| `executing-plans` | Use when you have a written implementation plan to execute in a separate session with review ch | +| `finishing-a-development-branch` | Use when implementation is complete, all tests pass, and you need to decide how to integrate th | +| `gsd-code-review` | Review source files changed during a phase for bugs, security issues, and code quality problems | +| `gsd-config` | Configure GSD settings — workflow toggles, advanced knobs, integrations, and model profile | +| `gsd-discuss-phase` | Gather phase context through adaptive questioning before planning. | +| `gsd-execute-phase` | Execute all plans in a phase with wave-based parallelization | +| `gsd-help` | Show available GSD commands and usage guide | +| `gsd-import` | Ingest external plans with conflict detection against project decisions before writing anything | +| `gsd-new-project` | Initialize a new project with deep context gathering and PROJECT.md | +| `gsd-pause-work` | Create context handoff when pausing work mid-phase | +| `gsd-phase` | CRUD for phases in ROADMAP.md — add, insert, remove, or edit phases | +| `gsd-plan-phase` | Create detailed phase plan (PLAN.md) with verification loop | +| `gsd-progress` | Check progress, advance workflow, or dispatch freeform intent — the unified GSD situational c | +| `gsd-quick` | Execute a quick task with GSD guarantees (atomic commits, state tracking) but skip optional age | +| `gsd-resume-work` | Resume work from previous session with full context restoration | +| `gsd-review` | Request cross-AI peer review of phase plans from external AI CLIs | +| `gsd-settings` | Configure GSD workflow toggles and model profile | +| `gsd-surface` | Toggle which skills are surfaced — apply a profile, list, or disable a cluster without reinst | +| `gsd-update` | Update GSD to latest version with changelog display | +| `gsd-verify-work` | Validate built features through conversational UAT | +| `gsd-workspace` | Manage GSD workspaces — create, list, or remove isolated workspace environments | +| `output-skill` | Overrides default LLM truncation behavior. Enforces complete code generation, bans placeholder | +| `plan-design-review` | Senior Designer review: rates each design dimension 0-10, explains what a 10 looks like, and fl | +| `pm-spec` | Product spec / PRD as a single page — problem, success metrics, scope, user stories, design n | +| `pr-feedback-quality-gate` | Safely track pull request feedback, resolve review comments or merge conflicts, validate fixes, | +| `receiving-code-review` | Use when receiving code review feedback, before implementing suggestions, especially if feedbac | +| `requesting-code-review` | Use when completing tasks, implementing major features, or before merging to verify work meets | +| `subagent-driven-development` | Use when executing implementation plans with independent tasks in the current session | +| `systematic-debugging` | Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes | +| `test-driven-development` | Use when implementing any feature or bugfix, before writing implementation code | +| `using-git-worktrees` | Use when starting feature work that needs isolation from current workspace or before executing | +| `using-superpowers` | Use when starting any conversation - establishes how to find and use skills, requiring skill in | +| `verification-before-completion` | Use when about to claim work is complete, fixed, or passing, before committing or creating PRs | +| `writing-plans` | Use when you have a spec or requirements for a multi-step task, before touching code | +| `writing-skills` | Use when creating new skills, editing existing skills, or verifying skills work before deployme | + +## 📊 研究·量化·數據 — 17 + +| Skill | 用途 | +|---|---| +| `caveman-claude-skill` | Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keepi | +| `d3-visualization` | Teaches the agent to produce D3 charts and interactive data visualizations. A comprehensive D3. | +| `dashboard` | Admin / analytics dashboard in a single HTML file. Fixed left sidebar, top bar with user/search | +| `data-report` | Turns CSV, Excel, or JSON data into a polished visual report page. | +| `dcf-valuation` | Discounted cash flow valuation and intrinsic value analysis for public companies. Use when the | +| `finance-report` | Quarterly / monthly financial report — masthead with KPIs, revenue and burn charts, P&L summa | +| `flowai-live-dashboard-template` | Team-management dashboard skill in the FlowAI aesthetic — three tabs (Team Members, Team Deta | +| `github-dashboard` | GitHub repository analytics dashboard — stars, forks, contributors, issues, pull requests, re | +| `ib-pitch-book` | Investment-banking pitch book for strategic alternatives — trading comps, precedent transacti | +| `last30days` | Recent community and social trend research over the last 30 days. Use when the brief asks what | +| `live-dashboard` | Notion-style team dashboard rendered as a Live Artifact. A single-page, self-contained HTML das | +| `research-decision-room` | Turn messy user research notes, interviews, support tickets, surveys, and product context into | +| `social-media-dashboard` | Creator-facing social media analytics dashboard in a single HTML file. A platform switcher (X / | +| `social-media-matrix-tracker-template` | 社媒矩阵数据追踪面板模板(Social Media Matrix Tracker)。 Use when users ask for | +| `swiss-user-research-video-template` | Swiss-style user-research narrative template in warm-paper editorial aesthetics. Use when users | +| `trading-analysis-dashboard-template` | Professional trading analysis dashboard template (single-file HTML) with light/dark theme switc | +| `x-research` | X/Twitter public sentiment research for recent market, company, product, or community discourse | + +## 🎬 影片·YouTube — 25 + +| Skill | 用途 | +|---|---| +| `8-bit-orbit-video-template` | Hyperframes-based video template for retro pixel deck motion design. Use when users want a high | +| `fal-kling-o3` | Generate images and videos with Kling O3 — Kling's most powerful model family — via fal.ai. | +| `fal-lip-sync` | Create talking head videos and lip sync audio to video via fal.ai. Useful for explainer avatars | +| `fal-video-edit` | Edit existing videos using AI — remix style, upscale, remove background, and add audio via fa | +| `frame-data-chart-nyt` | NYT-newsroom typography, staggered reveal animation, and editorial-grade charts (line, bar, or | +| `frame-flowchart-sticky` | SVG curve connectors, sticky-note nodes, and cursor interaction with a whiteboard-brainstorm fe | +| `frame-glitch-title` | Digital glitch, chromatic offset, and data-corruption title frame for video transitions or cybe | +| `frame-light-leak-cinema` | Film light leaks, grain, 16:9 letterbox, and large serif type for cinematic openings or chapter | +| `frame-liquid-bg-hero` | WebGL-style fluid displacement background with a quote overlay, suited to video intros, landing | +| `frame-logo-outro` | Segmented logo assembly, glow bloom, and tagline reveal for video outros or brand closing frame | +| `frame-macos-notification` | Realistic macOS notification banner with app icon, title, and body, suited to video overlays or | +| `gif-sticker-maker` | Convert photos into animated GIF stickers in Funko Pop / Pop Mart style via the MiniMax API. Us | +| `hyperframes` | Create video compositions, animations, title cards, overlays, captions, voiceovers, audio-react | +| `motion-frames` | A single-frame motion-design composition with looping CSS animations — rotating type ring, an | +| `remotion` | Programmatic video creation with React. Useful for branded explainers, social cuts, dashboards- | +| `slack-gif-creator` | Create animated GIFs optimized for Slack with validators for size constraints and composable an | +| `sora` | Generate, remix, and manage short video clips via OpenAI's Sora API. Useful for cinematic shots | +| `sprite-animation` | A pixel / sprite-style animated explainer slide — full-bleed cream stage, bold display year, | +| `venice-video` | Video generation and transcription workflows via the Venice.ai API. | +| `vfx-text-cursor` | Cursor light trail, chromatic rays, and directional flares for word-by-word quote reveals in vi | +| `video-downloader` | Download videos from YouTube and other platforms for offline viewing, editing, or archival with | +| `video-hyperframes` | Hyperframes / Remotion-compatible continuous frame animation with autoplay support. | +| `video-shortform` | Short-form video generation skill — 3-10 second clips for product reveals, motion teasers, am | +| `weread-year-in-review-video-template` | WeRead-inspired HyperFrames video template for vertical annual reading reports, personal readin | +| `youtube-clipper` | YouTube 视频智能剪辑工具。下载视频和字幕,AI 分析生成精细章节(几分 | + +## 📑 簡報·PPT·Deck — 64 + +| Skill | 用途 | +|---|---| +| `deck-guizang-editorial` | Editorial magazine meets e-ink: 10 layouts and 5 palettes (Ink, Indigo Porcelain, Forest Ink, K | +| `deck-open-slide-canvas` | Locked 1920x1080 canvas deck with React component-level free composition, not bound to a fixed | +| `deck-swiss-international` | 16-column grid, one saturated accent, and 22 locked layouts (Klein Blue, Lemon, Mint, Safety Or | +| `frontend-slides` | Generate animation-rich HTML presentations with visual style previews. Useful for online keynot | +| `guizang-ppt` | Generates an editorial magazine × electronic ink style horizontal-swipe web deck (a single HTM | +| `html-ppt-course-module` | Online-course / workshop module deck — warm paper background + Playfair serif, persistent lef | +| `html-ppt-graphify-dark-graph` | 暗底知识图谱 deck — #06060c→#0e1020 深夜渐变 + 漂浮 blur orbs、封面 SVG 力 | +| `html-ppt-hermes-cyber-terminal` | 暗终端 honest-review deck — #0a0c10 黑底 + 56px 赛博网格 + CRT 暗角 + 扫描线、 | +| `html-ppt-knowledge-arch-blueprint` | 奶油蓝图架构 deck — 奶油纸 #F0EAE0 底色 + 单一锈红 #B5392A 高亮、48px 蓝 | +| `html-ppt-obsidian-claude-gradient` | GitHub 暗紫渐变 deck — GitHub-dark #0d1117 + 紫蓝 radial 环境光 + 60px 网格 mask | +| `html-ppt-pitch-deck` | Investor-ready 10-slide HTML pitch deck — white + blue→purple gradient hero, big numbers, t | +| `html-ppt-presenter-mode-reveal` | 演讲者模式专用 deck — tokyo-night 默认主题,5 套主题 T 键切换,每页带 1 | +| `html-ppt-product-launch` | Launch keynote deck — dark hero + light content, warm orange→peach accent, feature cards, p | +| `html-ppt-retro-quarterly-review` | Retro Quarterly Review presentation template in a bold blue + orange editorial language. Use wh | +| `html-ppt-taste-brutalist` | 16:9 HTML deck in tactical-telemetry / CRT-terminal taste. Deactivated-CRT charcoal slides, whi | +| `html-ppt-taste-editorial` | 16:9 HTML deck in editorial-minimalist taste. Warm cream slides, serif display + grotesque body | +| `html-ppt-tech-sharing` | Conference / internal tech-talk deck — GitHub-dark, JetBrains Mono, terminal code blocks, age | +| `html-ppt-testing-safety-alert` | 红琥珀警示 deck — 顶/底 45° 红黑 hazard 条纹、红色删除线否定标题、L1/L | +| `html-ppt-weekly-report` | Team weekly / status-update deck — corporate clarity, 8-cell KPI grid, shipped list, 8-week b | +| `html-ppt-xhs-pastel-card` | 柔和马卡龙慢生活 deck — 奶油 #fef8f1 底 + 三个柔光 blob、Playfair 斜体衬 | +| `html-ppt-xhs-white-editorial` | 白底杂志风 deck — 纯白背景 + 顶部 10 色彩虹 bar、80-110px display 标题、紫 | +| `html-ppt-zhangzara-8-bit-orbit` | 8-Bit Orbit — Pixel-art neon arcade aesthetic on a deep navy void. Anything that should feel | +| `html-ppt-zhangzara-biennale-yellow` | Biennale Yellow — Solar yellow on warm parchment with deep indigo serif and atmospheric sun-g | +| `html-ppt-zhangzara-block-frame` | BlockFrame — Neobrutalist deck with pastel-neon color blocks and chunky black borders. Anythi | +| `html-ppt-zhangzara-blue-professional` | Blue Professional — Cream paper background with electric cobalt blue accents; clean modern pr | +| `html-ppt-zhangzara-bold-poster` | Bold Poster — Editorial poster aesthetic with massive Shrikhand display and a single fire-eng | +| `html-ppt-zhangzara-broadside` | Broadside — Dark editorial canvas with a single fire orange accent and bilingual Latin/Chines | +| `html-ppt-zhangzara-capsule` | Capsule — Modular pill-shaped cards on warm bone with a full pastel-pop palette. Anything tha | +| `html-ppt-zhangzara-cartesian` | Cartesian — Quiet warm-neutral palette with classical Playfair serifs; tasteful and unhurried | +| `html-ppt-zhangzara-cobalt-grid` | Cobalt Grid — Electric cobalt italic serifs on a graph-paper canvas, anchored by stair-steppe | +| `html-ppt-zhangzara-coral` | Coral — Cream and coral on near-black, set in oversized Bebas Neue. Anything that should feel | +| `html-ppt-zhangzara-creative-mode` | Creative Mode — Cream paper canvas with confident multi-color (green, pink, orange, yellow) a | +| `html-ppt-zhangzara-daisy-days` | Daisy Days — Cheerful pastel deck with hand-drawn daisies, stars, and rainbows. Friendly, sof | +| `html-ppt-zhangzara-editorial-tri-tone` | Editorial Tri-Tone — Three-color editorial system: dusty pink, mustard cream, and deep burgun | +| `html-ppt-zhangzara-grove` | Grove — Forest-green canvas with cream type, classical Playfair serifs, and a single rust acc | +| `html-ppt-zhangzara-long-table` | Long Table — Warm cream and rust-red supper-club aesthetic with bold uppercase grotesk headli | +| `html-ppt-zhangzara-mat` | Mat — Dark sage canvas with bone paper and burnt-orange accent; mid-century modern with wood | +| `html-ppt-zhangzara-monochrome` | Monochrome — Ivory ledger paper with all-black type; Lora serif headlines, Jost body, no colo | +| `html-ppt-zhangzara-neo-grid-bold` | Neo-Grid Bold — Editorial neo-brutalism with a single neon yellow accent on off-white paper. | +| `html-ppt-zhangzara-peoples-platform` | People's Platform (Block & Bold) — Activist poster energy: blue, orange, red on cream, with A | +| `html-ppt-zhangzara-pin-and-paper` | Pin & Paper — Yellow paper with safety-pin illustrations, ink-blue handwritten Caveat, paper- | +| `html-ppt-zhangzara-pink-script` | Pink Script — After Hours — Black canvas, hot pink accent, pearl-cream paper, Instrument Se | +| `html-ppt-zhangzara-playful` | Playful — Sun-warm peach background with Syne display: a friendly indie launch deck. Anything | +| `html-ppt-zhangzara-raw-grid` | Raw Grid — Neo-brutalist deck with thick borders, offset shadows, and a pink/sage/ink palette | +| `html-ppt-zhangzara-retro-windows` | Retro Windows — Windows 95 chrome: gray title bars, MS Sans Serif, pixel typography, full nos | +| `html-ppt-zhangzara-retro-zine` | Retro Zine — Beige paper with green accent and Bebas Neue + Caveat: a riso-printed zine in HT | +| `html-ppt-zhangzara-sakura-chroma` | Sakura Chroma — Vintage Japanese cassette-package aesthetic: cream paper, diagonal rainbow ri | +| `html-ppt-zhangzara-scatterbrain` | Scatterbrain — Post-it inspired: pastel sticky notes, Caveat handwriting, Shrikhand and Zilla | +| `html-ppt-zhangzara-signal` | Signal — Deep navy canvas with bone paper and a single muted-gold accent; institutional with | +| `html-ppt-zhangzara-soft-editorial` | Soft Editorial — Cormorant Garamond serif on warm paper with sage, blush, and lemon accents. | +| `html-ppt-zhangzara-stencil-tablet` | Stencil & Tablet — Bone paper with stencil-cut headlines and a six-color earth palette: archa | +| `html-ppt-zhangzara-studio` | Studio — Black canvas with electric-yellow type; high-voltage design studio aesthetic. Anythi | +| `html-ppt-zhangzara-vellum` | Vellum — Deep navy canvas with warm-yellow italic Cormorant serifs and a single dusty teal ac | +| `html-ppt` | HTML PPT Studio — author professional static HTML presentations in many styles, layouts, and | +| `kami-deck` | Produce a print-grade slide deck in the kami (紙 / 纸) design system — warm parchment backg | +| `nanobanana-ppt` | AI-powered PPT generation with document analysis and styled images via the NanoBanana stack. Co | +| `open-design-landing-deck` | Produce a single-file slide deck in the Atelier Zero visual language (warm-paper background, it | +| `ppt-keynote` | Apple Keynote-quality slides, one card per screen, with keyboard left/right navigation. | +| `pptx-generator` | Create and edit PowerPoint presentations from scratch with PptxGenJS — MiniMax's production-t | +| `pptx-html-fidelity-audit` | Audit a python-pptx export against its source HTML deck, identify layout/content drift (footer | +| `pptx` | Read, generate, and adjust PowerPoint slides, layouts, and templates. Useful for executive deck | +| `replit-deck` | Single-file horizontal-swipe HTML deck in the style of Replit Slides's landing-page template ga | +| `simple-deck` | Single-file horizontal-swipe HTML deck. Built by copying the seed `assets/template.html` (which | +| `slides` | Create and edit .pptx presentation decks with PptxGenJS. Useful for sales decks, kickoff briefs | + +## 🎨 設計擷取·品牌 — 11 + +| Skill | 用途 | +|---|---| +| `brand-extract` | Extract a complete Brand Kit from a live website by driving the in-app browser. Use when a bran | +| `brand-guidelines` | Apply Anthropic's official brand colors and typography to artifacts for consistent visual ident | +| `brandkit` | Premium brand-kit image generation skill for creating high-end brand-guidelines boards, logo sy | +| `color-expert` | Color science expert skill with 286K words of reference material covering OKLCH/OKLAB, palette | +| `competitive-ads-extractor` | Extract and analyze competitors' ads from ad libraries to understand messaging and creative app | +| `design-brief` | Parse a structured design brief written in I-Lang protocol format into a concrete design spec. | +| `design-consultation` | Build a complete design system from scratch with creative risks and realistic product mockups. | +| `design-md` | Create and manage DESIGN.md files. Useful for capturing design direction, tokens, and visual ru | +| `extract-design` | Extract the full design language from any website URL. Produces 8 output files including AI-opt | +| `reference-design-contract` | Turn vague taste, screenshots, URLs, product notes, or make it feel like this references into a | +| `theme-factory` | Apply professional font and color themes to artifacts including slides, docs, reports, and HTML | + +## 🖌️ Figma — 7 + +| Skill | 用途 | +|---|---| +| `figma-code-connect-components` | Connect Figma design components to code components using Code Connect so design-system updates | +| `figma-create-design-system-rules` | Generate project-specific design system rules for Figma-to-code workflows. Useful for capturing | +| `figma-create-new-file` | Create a new blank Figma Design or FigJam file. Useful as the first step in scripted design-sys | +| `figma-generate-design` | Build or update screens in Figma from code or description using design system components. Trans | +| `figma-generate-library` | Build or update a professional-grade design system library in Figma from a codebase. Useful for | +| `figma-implement-design` | Translate Figma designs into production-ready code with 1:1 visual fidelity. Useful for handing | +| `figma-use` | Run Figma Plugin API scripts for canvas writes, inspections, variables, and design-system work. | + +## 🖼️ 圖像生成 — 26 + +| Skill | 用途 | +|---|---| +| `algorithmic-art` | Create generative art using p5.js with seeded randomness so every render is reproducible. Usefu | +| `canvas-design` | Create beautiful visual art in PNG and PDF documents using design philosophy and aesthetic prin | +| `ecommerce-image-workflow` | Reference-product ecommerce image workflow for generating a compact set of product-faithful mai | +| `fal-3d` | Generate 3D models from text or images via fal.ai. Useful for game assets, AR previews, product | +| `fal-generate` | Generate images and videos using fal.ai AI models. Production-grade catalogue covering Flux, SD | +| `fal-image-edit` | AI-powered image editing with style transfer, background removal, object removal, and inpaintin | +| `fal-realtime` | Real-time and streaming AI image generation via fal.ai. Suited for moodboard exploration, draft | +| `fal-restore` | Restore and fix image quality — deblur, denoise, fix faces, and restore old documents using f | +| `fal-train` | Train custom AI models (LoRA) on fal.ai for personalized image generation tailored to a brand, | +| `fal-tryon` | Virtual try-on — see how clothes look on a person via fal.ai's hosted try-on models. Useful f | +| `fal-upscale` | Upscale and enhance image and video resolution using AI super-resolution models hosted on fal.a | +| `fal-vision` | Analyze images — segment objects, detect, run OCR, describe, and answer visual questions via | +| `image-enhancer` | Improve image and screenshot quality by enhancing resolution, sharpness, and clarity for profes | +| `image-poster` | Single-image generation skill for posters, key art, and editorial illustrations. Defaults to gp | +| `image-to-code-skill` | Elite website image-to-code skill for Codex. For visually important web tasks, it must first ge | +| `imagegen-frontend-mobile` | Elite mobile app image-generation skill for creating premium, app-native screen concepts and fl | +| `imagegen-frontend-web` | Elite frontend image-direction skill for generating premium, conversion-aware website design re | +| `imagegen` | Generate and edit images using OpenAI's Image API for project assets — UI mockups, icons, ill | +| `imagen` | Generate images using Google Gemini's image generation API for UI mockups, icons, illustrations | +| `magazine-poster` | An editorial-style poster — newsprint paper, dateline, oversized serif headline with a struck | +| `mockup-device-3d` | Static iPhone and MacBook 3D-style showcase with real HTML embedded on screens, glass-lens refr | +| `pixelbin-media` | Generate and edit images and videos with an 85+ API portfolio and build visually appealing webs | +| `poster-hero` | Vertical poster or Moments-style share image with strong visual impact. | +| `replicate` | Discover, compare, and run AI models using Replicate's API. Strong fit for image, audio, and vi | +| `venice-image-edit` | Image edits, upscaling, and background removal via the Venice.ai API. | +| `venice-image-generate` | Image generation endpoints and available styles via the Venice.ai API. | + +## 🔊 音訊·語音 — 7 + +| Skill | 用途 | +|---|---| +| `ai-music-album` | Full-lifecycle AI music album production — concept, lyric drafting, track sequencing, and exp | +| `audio-jingle` | Audio generation skill — jingles, beds, voiceover, and sound effects. Routes music requests t | +| `minimax-docx` | Professional DOCX document creation and editing using OpenXML SDK. Useful for branded reports, | +| `minimax-pdf` | Generate, fill, and reformat PDFs with a token-based design system and 15 cover styles. Useful | +| `speech` | Generate spoken audio from text using OpenAI's API with built-in voices. Useful for narrated ex | +| `venice-audio-music` | Music generation queueing, retrieval, and completion endpoints via Venice.ai. Suited for jingle | +| `venice-audio-speech` | Text-to-speech models, voices, formats, and streaming via Venice.ai. Useful for narration, voic | + +## 📣 社群·行銷·轉換 — 15 + +| Skill | 用途 | +|---|---| +| `ad-creative` | Generate and iterate ad creative including headlines, descriptions, and primary text. Useful fo | +| `card-twitter` | Twitter quote or data card designed to pair with a post. | +| `card-xiaohongshu` | Xiaohongshu-style knowledge cards, arranged as a swipeable multi-card carousel. | +| `copywriting` | When the user wants to write, rewrite, or improve marketing copy for any page — including hom | +| `email-marketing` | A brand product-launch email — masthead with wordmark, hero image block, headline lockup with | +| `login-flow` | Mobile login and authentication flow screens | +| `marketing-psychology` | When the user wants to apply psychological principles, mental models, or behavioral science to | +| `paywall-upgrade-cro` | Design and optimize upgrade screens, paywalls, and upsell modals. Useful for SaaS conversion de | +| `pricing-page` | A standalone pricing page — header, plan tiers, feature comparison table, and an FAQ. Use whe | +| `saas-landing` | Single-page SaaS landing with hero, features, social proof, pricing, and CTA. Respects the acti | +| `social-carousel` | A three-card social-media carousel laid out as 1080×1080 squares — three cinematic, on-brand | +| `social-reddit-card` | Realistic Reddit post card with vote rail and comment count, suited to video overlays or story | +| `social-spotify-card` | Spotify Now Playing-style card with album art, progress bar, and playback controls, suited to v | +| `social-x-post-card` | Realistic X post card with engagement metrics (likes, reposts, views), suited to video overlays | +| `waitlist-page` | Minimal pre-launch landing with email capture, brand logo, and optional decorative layer. Reads | + +## 💻 前端·UI 設計 — 36 + +| Skill | 用途 | +|---|---| +| `apple-hig` | Apple Human Interface Guidelines as 14 agent skills covering platforms, foundations, components | +| `artifacts-builder` | Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern fr | +| `brutalist-skill` | Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rig | +| `emilkowalski-motion` | Motion-design follow-up skill inspired by Emil Kowalski's animation guidance. Use after an inte | +| `flutter-animating-apps` | Implement animated effects, transitions, and motion in Flutter apps. Useful for native iOS/Andr | +| `frontend-design` | Create distinctive, production-grade frontend interfaces with strong visual direction, polished | +| `frontend-dev` | Full-stack frontend with cinematic animations, AI-generated media via MiniMax API, and generati | +| `frontend-skill` | Create visually strong landing pages, websites, and app UIs with restrained composition. OpenAI | +| `gpt-tasteskill` | Elite UX/UI & Advanced GSAP Motion Engineer. Enforces Python-driven true randomization for layo | +| `gsap-core` | Official GSAP skill for the core API — gsap.to(), from(), fromTo(), easing, duration, stagger | +| `gsap-frameworks` | Official GSAP skill for Vue, Svelte, and other non-React frameworks — lifecycle, scoping sele | +| `gsap-performance` | Official GSAP skill for performance — prefer transforms, avoid layout thrashing, will-change, | +| `gsap-plugins` | Official GSAP skill for GSAP plugins — registration, ScrollToPlugin, ScrollSmoother, Flip, Dr | +| `gsap-react` | Official GSAP skill for React — useGSAP hook, refs, gsap.context(), cleanup. Use when the use | +| `gsap-scrolltrigger` | Official GSAP skill for ScrollTrigger — scroll-linked animations, pinning, scrub, triggers. U | +| `gsap-timeline` | Official GSAP skill for timelines — gsap.timeline(), position parameter, nesting, playback. U | +| `gsap-utils` | Official GSAP skill for gsap.utils — clamp, mapRange, normalize, interpolate, random, snap, t | +| `hand-drawn-diagrams` | Generate hand-drawn Excalidraw diagrams from a prompt — animated SVG, hosted edit link, and P | +| `minimalist-skill` | Clean editorial-style interfaces. Warm monochrome palette, typographic contrast, flat bento gri | +| `redesign-skill` | Upgrades existing websites and apps to premium quality. Audits current design, identifies gener | +| `shadcn-ui` | Build UI components with shadcn/ui. Pairs with the Stitch design loop to ship structured, acces | +| `shader-dev` | GLSL shader techniques for ray marching, fluid simulation, particle systems, and procedural gen | +| `soft-skill` | Teaches the AI to design like a high-end agency. Defines the exact fonts, spacing, shadows, car | +| `swiftui-design` | SwiftUI 前端设计 skill — anti AI-slop rules, design direction advisor, brand asset protoc | +| `taste-skill-v1` | The original v1 taste-skill, preserved for projects depending on its exact behavior. The curren | +| `taste-skill` | Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brie | +| `threejs` | Three.js skills for creating 3D elements and interactive experiences in the browser — scenes, | +| `ui-skills` | Opinionated, evolving constraints to guide agents when building interfaces. Useful for keeping | +| `ui-ux-pro-max` | Catalog-only UI/UX Pro Max entry. The full upstream templates, data, and search workflow are no | +| `web-artifacts-builder` | Build complex claude.ai HTML artifacts with React and Tailwind. Anthropic's reference workflow | +| `web-design-guidelines` | Web design guidelines and standards by the Vercel engineering team. Covers layout, typography, | +| `web-prototype-taste-brutalist` | Swiss industrial-print web prototype. Newsprint canvas, monolithic black grotesque, viewport-bl | +| `web-prototype-taste-editorial` | Editorial-minimalist web prototype. Warm monochrome canvas, serif display + grotesque body, 1px | +| `web-prototype-taste-soft` | Apple-tier soft web prototype. Silver/cream canvas, double-bezel cards, button-in-button CTAs, | +| `web-prototype` | General-purpose desktop web prototype. Single self-contained HTML file built by copying the see | +| `wireframe-sketch` | A hand-drawn wireframe exploration — graph-paper background, marker / pencil tone, multiple t | + +## 📱 App·元件·儀表板 — 9 + +| Skill | 用途 | +|---|---| +| `contact-widget` | Self-contained floating chat widget with welcome screen, social links, meeting button, and mess | +| `gamified-app` | A multi-frame gamified mobile-app prototype — three phone frames on a dark showcase stage. Fr | +| `hatch-pet` | Create, repair, validate, preview, and package Codex-compatible animated pet spritesheets from | +| `kanban-board` | Kanban / task board with columns (To do / In progress / In review / Done), draggable-looking ca | +| `live-artifact` | Create refreshable, auditable Open Design artifacts backed by connector or local data. Trigger | +| `mobile-app` | A mobile-app screen rendered inside a pixel-accurate iPhone 15 Pro frame on the page. Built by | +| `mobile-onboarding` | A multi-screen mobile onboarding flow rendered as three phone frames side by side — splash, v | +| `platform-design` | 300+ design rules from Apple HIG, Material Design 3, and WCAG 2.2 for cross-platform apps. Usef | +| `team-okrs` | OKR tracker page — quarter banner, three objectives with their key results as progress bars, | + +## 🔗 整合 Orbit — 5 + +| Skill | 用途 | +|---|---| +| `orbit-general` | Open Orbit briefing skill — selected by the Orbit pipeline when the user has two or more conn | +| `orbit-github` | Open Orbit briefing skill — selected by the Orbit pipeline when GitHub is the user's only con | +| `orbit-gmail` | Open Orbit briefing skill — selected by the Orbit pipeline when Gmail is the user's only conn | +| `orbit-linear` | Open Orbit briefing skill — selected by the Orbit pipeline when Linear is the user's only con | +| `orbit-notion` | Open Orbit briefing skill — selected by the Orbit pipeline when Notion is the user's only con | + +## 🌐 瀏覽器·自動化 — 6 + +| Skill | 用途 | +|---|---| +| `agent-browser` | Browser automation CLI for AI agents. Use when the user needs to inspect, test, or automate bro | +| `browser-harness` | Always use browser-harness for any web interaction: automation, scraping, testing, or site/app | +| `export-download-debugging` | Diagnose and fix browser, preview, or Electron export/download failures, especially image expor | +| `full-page-screenshot` | Capture full-page screenshots of web pages via Chrome DevTools Protocol with zero dependencies. | +| `screenshots-marketing` | Generate marketing screenshots with Playwright. Useful for landing-page hero shots, App Store s | +| `screenshot` | Capture desktop, app windows, or pixel regions across OS platforms. Useful for marketing screen | + +## 📄 文件·Office — 17 + +| Skill | 用途 | +|---|---| +| `article-magazine` | Huashu / huashu-md-html-inspired magazine article layout for turning Markdown or notes into a p | +| `blog-post` | A long-form article / blog post — masthead, hero image placeholder, article body with figures | +| `clinical-case-report` | Structured medical case presentation for clinical rounds, conferences, and documentation. Gener | +| `digital-eguide` | A two-spread digital e-guide preview — page 1 is a cover (display title, author, What's insid | +| `doc-kami-parchment` | Warm parchment canvas (#f5f4ed), monochrome ink-blue accent (#1B365D), one serif family, and ed | +| `docs-page` | A documentation page — inline-start nav, scrollable article body, inline-end table of content | +| `docx` | Create, edit, and analyze Word documents with tracked changes, comments, and formatting. Useful | +| `doc` | Read, create, and edit .docx documents with formatting and layout fidelity via OpenAI's documen | +| `eng-runbook` | An engineering runbook — service overview, alerts table, dashboards links, common procedures | +| `faq-page` | A Frequently Asked Questions (FAQ) page with collapsible accordion sections, search functionali | +| `field-notes-editorial-template` | Editorial Field Notes report template with soft paper background, serif hero typography, rounde | +| `hr-onboarding` | A new-hire onboarding plan as a single page — first week schedule, buddy + manager intro, lea | +| `invoice` | A printable invoice page — sender + recipient block, line items table, tax breakdown, totals, | +| `meeting-notes` | Meeting notes page — title bar with attendees, agenda checklist, decisions block, action item | +| `pdf` | Extract text, create PDFs, and handle forms. Useful for press releases, branded one-pagers, and | +| `release-notes-one-pager` | Release notes one-page HTML with highlights, Added, Fixed, Breaking changes, Known issues, and | +| `resume-modern` | Modern minimal resume, single A4 page, ready for print or PDF export. | + +## 📦 其他·雜項 — 17 + +| Skill | 用途 | +|---|---| +| `after-hours-editorial-template` | Luxury dark-editorial HyperFrames template for three-page cinematic storyboards, inspired by ha | +| `creative-director` | AI creative director with recursive self-assessment: 20+ methodologies (SIT, TRIZ, Bisociation, | +| `critique` | Run a 5-dimension expert design review on any HTML artifact in the project — Philosophy / Vis | +| `dating-web` | A consumer-feeling dating / matchmaking dashboard — left rail navigation, ticker bar of commu | +| `digits-fintech-swiss-template` | Swiss-grid fintech deck template in black / warm paper / neon-lime contrast. Use when users ask | +| `domain-name-brainstormer` | Generate creative domain name ideas and check availability across multiple TLDs including .com, | +| `editorial-burgundy-principles-template` | Editorial studio deck template in burgundy / blush / muted-gold palette. Use when users ask for | +| `impeccable-design-polish` | Follow-up design polish skill inspired by Impeccable. Use after a web or HTML artifact exists t | +| `kami-landing` | Produce a print-grade single-page kami (紙 / 纸) document — warm parchment canvas, ink-blue | +| `open-design-landing` | Produce a world-class single-page editorial landing site in the Atelier Zero visual language (M | +| `stitch-loop` | Iterative design-to-code feedback loop. Critique → adjust → ship cycle for tightening visua | +| `stitch-skill` | Semantic Design System Skill for Google Stitch. Generates agent-friendly DESIGN.md files that e | +| `swiss-creative-mode-template` | Swiss-inspired creative-mode presentation template skill with bold editorial typography, high-c | +| `tweaks` | Wrap any HTML artifact with a side panel of live, parameterized controls — accent color, type | +| `weekly-update` | Single-file horizontal-swipe slide deck for a weekly team update — shipped, in flight, blocke | +| `wpds` | WordPress Design System. Apply WordPress's official design tokens, typography, and component pa | +| `youtube-thumbnail-design` | YouTube thumbnail design with specific dimensions, contrast rules, and mobile preview optimizat | diff --git a/docs/skills-map.md b/docs/skills-map.md new file mode 100644 index 0000000..be46216 --- /dev/null +++ b/docs/skills-map.md @@ -0,0 +1,119 @@ +# Skills 分類地圖(共 301 個 · D:\claude\skills 全域) + +> 2026-06-25 建立、同日清理幻影後 301 個,全部保留、按類別分群。 +> **用法:Carson 提到某主題,我會自動從對應類別挑 skill 直接用,不必打斜線**(見記憶 [[skill-auto-use-map]])。 +> 要自己找:下面按類別瀏覽即可。原始碼留在 `D:\claude\external-tools`。 + +## 🛠️ 工作流與開發(Superpowers/GSD/SuperClaude 系) — 20 個 + +- brainstorming - design-review - dispatching-parallel-agents - enhance-prompt - executing-plans - +finishing-a-development-branch - output-skill - plan-design-review - pm-spec - pr-feedback-quality-gate - +receiving-code-review - requesting-code-review - subagent-driven-development - systematic-debugging - +test-driven-development - using-git-worktrees - using-superpowers - verification-before-completion - +writing-plans - writing-skills + +## 📊 研究·量化·數據 — 17 個 + +- caveman-claude-skill - d3-visualization - dashboard - data-report - dcf-valuation - finance-report - +flowai-live-dashboard-template - github-dashboard - ib-pitch-book - last30days - live-dashboard - +research-decision-room - social-media-dashboard - social-media-matrix-tracker-template - +swiss-user-research-video-template - trading-analysis-dashboard-template - x-research + +## 🎬 影片·YouTube — 25 個 + +- 8-bit-orbit-video-template - fal-kling-o3 - fal-lip-sync - fal-video-edit - frame-data-chart-nyt - +frame-flowchart-sticky - frame-glitch-title - frame-light-leak-cinema - frame-liquid-bg-hero - +frame-logo-outro - frame-macos-notification - gif-sticker-maker - hyperframes - motion-frames - remotion - +slack-gif-creator - sora - sprite-animation - venice-video - vfx-text-cursor - video-downloader - +video-hyperframes - video-shortform - weread-year-in-review-video-template - youtube-clipper + +## 📑 簡報·PPT·Deck — 64 個 + +- deck-guizang-editorial - deck-open-slide-canvas - deck-swiss-international - frontend-slides - guizang-ppt +- html-ppt - html-ppt-course-module - html-ppt-graphify-dark-graph - html-ppt-hermes-cyber-terminal - +html-ppt-knowledge-arch-blueprint - html-ppt-obsidian-claude-gradient - html-ppt-pitch-deck - +html-ppt-presenter-mode-reveal - html-ppt-product-launch - html-ppt-retro-quarterly-review - +html-ppt-taste-brutalist - html-ppt-taste-editorial - html-ppt-tech-sharing - html-ppt-testing-safety-alert - +html-ppt-weekly-report - html-ppt-xhs-pastel-card - html-ppt-xhs-white-editorial - +html-ppt-zhangzara-8-bit-orbit - html-ppt-zhangzara-biennale-yellow - html-ppt-zhangzara-block-frame - +html-ppt-zhangzara-blue-professional - html-ppt-zhangzara-bold-poster - html-ppt-zhangzara-broadside - +html-ppt-zhangzara-capsule - html-ppt-zhangzara-cartesian - html-ppt-zhangzara-cobalt-grid - +html-ppt-zhangzara-coral - html-ppt-zhangzara-creative-mode - html-ppt-zhangzara-daisy-days - +html-ppt-zhangzara-editorial-tri-tone - html-ppt-zhangzara-grove - html-ppt-zhangzara-long-table - +html-ppt-zhangzara-mat - html-ppt-zhangzara-monochrome - html-ppt-zhangzara-neo-grid-bold - +html-ppt-zhangzara-peoples-platform - html-ppt-zhangzara-pin-and-paper - html-ppt-zhangzara-pink-script - +html-ppt-zhangzara-playful - html-ppt-zhangzara-raw-grid - html-ppt-zhangzara-retro-windows - +html-ppt-zhangzara-retro-zine - html-ppt-zhangzara-sakura-chroma - html-ppt-zhangzara-scatterbrain - +html-ppt-zhangzara-signal - html-ppt-zhangzara-soft-editorial - html-ppt-zhangzara-stencil-tablet - +html-ppt-zhangzara-studio - html-ppt-zhangzara-vellum - kami-deck - nanobanana-ppt - open-design-landing-deck +- ppt-keynote - pptx - pptx-generator - pptx-html-fidelity-audit - replit-deck - simple-deck - slides + +## 🎨 設計擷取·品牌 — 11 個 + +- brand-extract - brand-guidelines - brandkit - color-expert - competitive-ads-extractor - design-brief - +design-consultation - design-md - extract-design - reference-design-contract - theme-factory + +## 🖌️ Figma — 7 個 + +- figma-code-connect-components - figma-create-design-system-rules - figma-create-new-file - +figma-generate-design - figma-generate-library - figma-implement-design - figma-use + +## 🖼️ 圖像生成 — 26 個 + +- algorithmic-art - canvas-design - ecommerce-image-workflow - fal-3d - fal-generate - fal-image-edit - +fal-realtime - fal-restore - fal-train - fal-tryon - fal-upscale - fal-vision - image-enhancer - image-poster +- image-to-code-skill - imagegen - imagegen-frontend-mobile - imagegen-frontend-web - imagen - +magazine-poster - mockup-device-3d - pixelbin-media - poster-hero - replicate - venice-image-edit - +venice-image-generate + +## 🔊 音訊·語音 — 7 個 + +- ai-music-album - audio-jingle - minimax-docx - minimax-pdf - speech - venice-audio-music - +venice-audio-speech + +## 📣 社群·行銷·轉換 — 15 個 + +- ad-creative - card-twitter - card-xiaohongshu - copywriting - email-marketing - login-flow - +marketing-psychology - paywall-upgrade-cro - pricing-page - saas-landing - social-carousel - +social-reddit-card - social-spotify-card - social-x-post-card - waitlist-page + +## 💻 前端·UI 設計 — 36 個 + +- apple-hig - artifacts-builder - brutalist-skill - emilkowalski-motion - flutter-animating-apps - +frontend-design - frontend-dev - frontend-skill - gpt-tasteskill - gsap-core - gsap-frameworks - +gsap-performance - gsap-plugins - gsap-react - gsap-scrolltrigger - gsap-timeline - gsap-utils - +hand-drawn-diagrams - minimalist-skill - redesign-skill - shadcn-ui - shader-dev - soft-skill - +swiftui-design - taste-skill - taste-skill-v1 - threejs - ui-skills - ui-ux-pro-max - web-artifacts-builder - +web-design-guidelines - web-prototype - web-prototype-taste-brutalist - web-prototype-taste-editorial - +web-prototype-taste-soft - wireframe-sketch + +## 📱 App·元件·儀表板 — 9 個 + +- contact-widget - gamified-app - hatch-pet - kanban-board - live-artifact - mobile-app - mobile-onboarding - +platform-design - team-okrs + +## 🔗 整合(Orbit:Gmail/GitHub/Notion/Linear) — 5 個 + +- orbit-general - orbit-github - orbit-gmail - orbit-linear - orbit-notion + +## 🌐 瀏覽器·自動化 — 6 個 + +- agent-browser - browser-harness - export-download-debugging - full-page-screenshot - +screenshot - screenshots-marketing + +## 📄 文件·Office — 17 個 + +- article-magazine - blog-post - clinical-case-report - digital-eguide - doc - doc-kami-parchment - docs-page +- docx - eng-runbook - faq-page - field-notes-editorial-template - hr-onboarding - invoice - meeting-notes - +pdf - release-notes-one-pager - resume-modern + +## 📦 其他·雜項 — 36 個 + +- after-hours-editorial-template - creative-director - critique - dating-web - digits-fintech-swiss-template +- domain-name-brainstormer - editorial-burgundy-principles-template - gsd-code-review - gsd-config - +gsd-discuss-phase - gsd-execute-phase - gsd-help - gsd-import - gsd-new-project - gsd-pause-work - gsd-phase +- gsd-plan-phase - gsd-progress - gsd-quick - gsd-resume-work - gsd-review - gsd-settings - gsd-surface - +gsd-update - gsd-verify-work - gsd-workspace - impeccable-design-polish - kami-landing - open-design-landing +- stitch-loop - stitch-skill - swiss-creative-mode-template - tweaks - weekly-update - wpds - +youtube-thumbnail-design + diff --git a/docs/skills.csv b/docs/skills.csv new file mode 100644 index 0000000..edee6ed --- /dev/null +++ b/docs/skills.csv @@ -0,0 +1,302 @@ +類別,Skill,用途 +"🛠️ 工作流與開發","brainstorming","""You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user inten" +"🛠️ 工作流與開發","design-review","Designer Who Codes: visual audit then fixes with atomic commits and before/after screenshots. Useful for tightening shipped UI before launch." +"🛠️ 工作流與開發","dispatching-parallel-agents","Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies" +"🛠️ 工作流與開發","enhance-prompt","Improve prompts with design specs and UI/UX vocabulary. Useful for design-to-code workflows and clarifying requests for visual output." +"🛠️ 工作流與開發","executing-plans","Use when you have a written implementation plan to execute in a separate session with review checkpoints" +"🛠️ 工作流與開發","finishing-a-development-branch","Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by prese" +"🛠️ 工作流與開發","gsd-code-review","""Review source files changed during a phase for bugs, security issues, and code quality problems""" +"🛠️ 工作流與開發","gsd-config","""Configure GSD settings — workflow toggles, advanced knobs, integrations, and model profile""" +"🛠️ 工作流與開發","gsd-discuss-phase","""Gather phase context through adaptive questioning before planning.""" +"🛠️ 工作流與開發","gsd-execute-phase","""Execute all plans in a phase with wave-based parallelization""" +"🛠️ 工作流與開發","gsd-help","""Show available GSD commands and usage guide""" +"🛠️ 工作流與開發","gsd-import","""Ingest external plans with conflict detection against project decisions before writing anything.""" +"🛠️ 工作流與開發","gsd-new-project","""Initialize a new project with deep context gathering and PROJECT.md""" +"🛠️ 工作流與開發","gsd-pause-work","""Create context handoff when pausing work mid-phase""" +"🛠️ 工作流與開發","gsd-phase","""CRUD for phases in ROADMAP.md — add, insert, remove, or edit phases""" +"🛠️ 工作流與開發","gsd-plan-phase","""Create detailed phase plan (PLAN.md) with verification loop""" +"🛠️ 工作流與開發","gsd-progress","""Check progress, advance workflow, or dispatch freeform intent — the unified GSD situational command""" +"🛠️ 工作流與開發","gsd-quick","""Execute a quick task with GSD guarantees (atomic commits, state tracking) but skip optional agents""" +"🛠️ 工作流與開發","gsd-resume-work","""Resume work from previous session with full context restoration""" +"🛠️ 工作流與開發","gsd-review","""Request cross-AI peer review of phase plans from external AI CLIs""" +"🛠️ 工作流與開發","gsd-settings","""Configure GSD workflow toggles and model profile""" +"🛠️ 工作流與開發","gsd-surface","""Toggle which skills are surfaced — apply a profile, list, or disable a cluster without reinstall""" +"🛠️ 工作流與開發","gsd-update","""Update GSD to latest version with changelog display""" +"🛠️ 工作流與開發","gsd-verify-work","""Validate built features through conversational UAT""" +"🛠️ 工作流與開發","gsd-workspace","""Manage GSD workspaces — create, list, or remove isolated workspace environments""" +"🛠️ 工作流與開發","output-skill","Overrides default LLM truncation behavior. Enforces complete code generation, bans placeholder patterns, and handles token-limit splits cleanly. Apply" +"🛠️ 工作流與開發","plan-design-review","Senior Designer review: rates each design dimension 0-10, explains what a 10 looks like, and flags AI Slop signals. Useful as a gate before merging UI" +"🛠️ 工作流與開發","pm-spec","Product spec / PRD as a single page — problem, success metrics, scope, user stories, design notes, rollout plan, open questions. Use when the brief " +"🛠️ 工作流與開發","pr-feedback-quality-gate","Safely track pull request feedback, resolve review comments or merge conflicts, validate fixes, and use a read-only cross-review before committing or " +"🛠️ 工作流與開發","receiving-code-review","Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires " +"🛠️ 工作流與開發","requesting-code-review","Use when completing tasks, implementing major features, or before merging to verify work meets requirements" +"🛠️ 工作流與開發","subagent-driven-development","Use when executing implementation plans with independent tasks in the current session" +"🛠️ 工作流與開發","systematic-debugging","Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes" +"🛠️ 工作流與開發","test-driven-development","Use when implementing any feature or bugfix, before writing implementation code" +"🛠️ 工作流與開發","using-git-worktrees","Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace ex" +"🛠️ 工作流與開發","using-superpowers","Use when starting any conversation - establishes how to find and use skills, requiring skill invocation before ANY response including clarifying quest" +"🛠️ 工作流與開發","verification-before-completion","Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming" +"🛠️ 工作流與開發","writing-plans","Use when you have a spec or requirements for a multi-step task, before touching code" +"🛠️ 工作流與開發","writing-skills","Use when creating new skills, editing existing skills, or verifying skills work before deployment" +"📊 研究·量化·數據","caveman-claude-skill","Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: " +"📊 研究·量化·數據","d3-visualization","Teaches the agent to produce D3 charts and interactive data visualizations. A comprehensive D3.js skill with examples across chart types and technique" +"📊 研究·量化·數據","dashboard","Admin / analytics dashboard in a single HTML file. Fixed left sidebar, top bar with user/search, main grid of KPI cards and one or two charts. Use whe" +"📊 研究·量化·數據","data-report","""Turns CSV, Excel, or JSON data into a polished visual report page.""" +"📊 研究·量化·數據","dcf-valuation","Discounted cash flow valuation and intrinsic value analysis for public companies. Use when the brief asks for DCF, fair value, intrinsic value, price " +"📊 研究·量化·數據","finance-report","Quarterly / monthly financial report — masthead with KPIs, revenue and burn charts, P&L summary table, top-line highlights, and an outlook paragraph" +"📊 研究·量化·數據","flowai-live-dashboard-template","Team-management dashboard skill in the FlowAI aesthetic — three tabs (Team Members, Team Details, Activity Log), KPI stat row, member table, role di" +"📊 研究·量化·數據","github-dashboard","GitHub repository analytics dashboard — stars, forks, contributors, issues, pull requests, recent activity, and top contributors. Use when the brief" +"📊 研究·量化·數據","ib-pitch-book","Investment-banking pitch book for strategic alternatives — trading comps, precedent transactions, valuation football field, DCF sensitivity, strateg" +"📊 研究·量化·數據","last30days","Recent community and social trend research over the last 30 days. Use when the brief asks what people are saying now, recent sentiment, community reac" +"📊 研究·量化·數據","live-dashboard","Notion-style team dashboard rendered as a Live Artifact. A single-page, self-contained HTML dashboard with KPIs, a 7-day sparkline, a real-time activi" +"📊 研究·量化·數據","research-decision-room","Turn messy user research notes, interviews, support tickets, surveys, and product context into an evidence-backed decision room: a single HTML artifac" +"📊 研究·量化·數據","social-media-dashboard","Creator-facing social media analytics dashboard in a single HTML file. A platform switcher (X / LinkedIn / YouTube / Instagram), a row of KPI cards (f" +"📊 研究·量化·數據","social-media-matrix-tracker-template","社媒矩阵数据追踪面板模板(Social Media Matrix Tracker)。 Use when users ask for a cinematic, data-dense social media analytics dashboa" +"📊 研究·量化·數據","swiss-user-research-video-template","Swiss-style user-research narrative template in warm-paper editorial aesthetics. Use when users ask for a premium research deck or story-first live ar" +"📊 研究·量化·數據","trading-analysis-dashboard-template","Professional trading analysis dashboard template (single-file HTML) with light/dark theme switch, dense market panels, chart interactions, demo/live p" +"📊 研究·量化·數據","x-research","X/Twitter public sentiment research for recent market, company, product, or community discourse. Use when the brief asks what people are saying on X, " +"🎬 影片·YouTube","8-bit-orbit-video-template","Hyperframes-based video template for retro pixel deck motion design. Use when users want a high-fidelity, multi-scene HTML-to-video composition with a" +"🎬 影片·YouTube","fal-kling-o3","Generate images and videos with Kling O3 — Kling's most powerful model family — via fal.ai." +"🎬 影片·YouTube","fal-lip-sync","Create talking head videos and lip sync audio to video via fal.ai. Useful for explainer avatars, multilingual dubbing previews, and social cuts." +"🎬 影片·YouTube","fal-video-edit","Edit existing videos using AI — remix style, upscale, remove background, and add audio via fal.ai's hosted video models." +"🎬 影片·YouTube","frame-data-chart-nyt","""NYT-newsroom typography, staggered reveal animation, and editorial-grade charts (line, bar, or range band).""" +"🎬 影片·YouTube","frame-flowchart-sticky","""SVG curve connectors, sticky-note nodes, and cursor interaction with a whiteboard-brainstorm feel.""" +"🎬 影片·YouTube","frame-glitch-title","""Digital glitch, chromatic offset, and data-corruption title frame for video transitions or cyberpunk heroes.""" +"🎬 影片·YouTube","frame-light-leak-cinema","""Film light leaks, grain, 16:9 letterbox, and large serif type for cinematic openings or chapter cards.""" +"🎬 影片·YouTube","frame-liquid-bg-hero","""WebGL-style fluid displacement background with a quote overlay, suited to video intros, landing heroes, or posters.""" +"🎬 影片·YouTube","frame-logo-outro","""Segmented logo assembly, glow bloom, and tagline reveal for video outros or brand closing frames.""" +"🎬 影片·YouTube","frame-macos-notification","""Realistic macOS notification banner with app icon, title, and body, suited to video overlays or product teasers.""" +"🎬 影片·YouTube","gif-sticker-maker","Convert photos into animated GIF stickers in Funko Pop / Pop Mart style via the MiniMax API. Useful for personalized chat stickers and avatar packs." +"🎬 影片·YouTube","hyperframes","Create video compositions, animations, title cards, overlays, captions, voiceovers, audio-reactive visuals, and scene transitions in HyperFrames HTML." +"🎬 影片·YouTube","motion-frames","A single-frame motion-design composition with looping CSS animations — rotating type ring, animated globe, ticking timer, parallax labels. Renders a" +"🎬 影片·YouTube","remotion","Programmatic video creation with React. Useful for branded explainers, social cuts, dashboards-to-video, and reproducible motion graphics." +"🎬 影片·YouTube","slack-gif-creator","Create animated GIFs optimized for Slack with validators for size constraints and composable animation primitives." +"🎬 影片·YouTube","sora","Generate, remix, and manage short video clips via OpenAI's Sora API. Useful for cinematic shots, b-roll, and rapid concept video iteration." +"🎬 影片·YouTube","sprite-animation","A pixel / sprite-style animated explainer slide — full-bleed cream stage, bold display year, animated pixel-art mascot (e.g. Hanafuda card, mushroom" +"🎬 影片·YouTube","venice-video","Video generation and transcription workflows via the Venice.ai API." +"🎬 影片·YouTube","vfx-text-cursor","""Cursor light trail, chromatic rays, and directional flares for word-by-word quote reveals in video intros.""" +"🎬 影片·YouTube","video-downloader","Download videos from YouTube and other platforms for offline viewing, editing, or archival with support for various formats and quality options." +"🎬 影片·YouTube","video-hyperframes","""Hyperframes / Remotion-compatible continuous frame animation with autoplay support.""" +"🎬 影片·YouTube","video-shortform","Short-form video generation skill — 3-10 second clips for product reveals, motion teasers, ambient loops. Defaults to Seedance 2 but works the same " +"🎬 影片·YouTube","weread-year-in-review-video-template","WeRead-inspired HyperFrames video template for vertical annual reading reports, personal reading dashboards, book-note recaps, and shareable year-in-r" +"🎬 影片·YouTube","youtube-clipper","YouTube 视频智能剪辑工具。下载视频和字幕,AI 分析生成精细章节(几分钟级别), 用户选择片段后自动剪辑、翻" +"📑 簡報·PPT·Deck","deck-guizang-editorial","""Editorial magazine meets e-ink: 10 layouts and 5 palettes (Ink, Indigo Porcelain, Forest Ink, Kraft Paper, Dune).""" +"📑 簡報·PPT·Deck","deck-open-slide-canvas","""Locked 1920x1080 canvas deck with React component-level free composition, not bound to a fixed template.""" +"📑 簡報·PPT·Deck","deck-swiss-international","""16-column grid, one saturated accent, and 22 locked layouts (Klein Blue, Lemon, Mint, Safety Orange).""" +"📑 簡報·PPT·Deck","frontend-slides","Generate animation-rich HTML presentations with visual style previews. Useful for online keynotes, embedded talks, and interactive briefs." +"📑 簡報·PPT·Deck","guizang-ppt","Generates an ""editorial magazine × electronic ink"" style horizontal-swipe web deck (a single HTML file), with a WebGL fluid background, serif headlin" +"📑 簡報·PPT·Deck","html-ppt-course-module","Online-course / workshop module deck — warm paper background + Playfair serif, persistent left sidebar of learning objectives, MCQ self-check page. " +"📑 簡報·PPT·Deck","html-ppt-graphify-dark-graph","暗底知识图谱 deck — #06060c→#0e1020 深夜渐变 + 漂浮 blur orbs、封面 SVG 力导向图谱、彩虹渐变标题、JetBrains Mono 命" +"📑 簡報·PPT·Deck","html-ppt-hermes-cyber-terminal","暗终端 honest-review deck — #0a0c10 黑底 + 56px 赛博网格 + CRT 暗角 + 扫描线、窗口红绿灯 chrome、`$ prompt` 命令行标题、" +"📑 簡報·PPT·Deck","html-ppt-knowledge-arch-blueprint","奶油蓝图架构 deck — 奶油纸 #F0EAE0 底色 + 单一锈红 #B5392A 高亮、48px 蓝图网格 mask、2px 黑边硬卡片、pipeline 步骤盒" +"📑 簡報·PPT·Deck","html-ppt-obsidian-claude-gradient","GitHub 暗紫渐变 deck — GitHub-dark #0d1117 + 紫蓝 radial 环境光 + 60px 网格 mask、居中布局、紫色 pill 标签、三色渐变标题" +"📑 簡報·PPT·Deck","html-ppt-pitch-deck","Investor-ready 10-slide HTML pitch deck — white + blue→purple gradient hero, big numbers, traction bar chart, $4.5M-style ask page. Use when the u" +"📑 簡報·PPT·Deck","html-ppt-presenter-mode-reveal","演讲者模式专用 deck — tokyo-night 默认主题,5 套主题 T 键切换,每页带 150-300 字逐字稿示例(