Skip to content

docs(ratewise): 新增匯率 API 語意 v3 PRD 與 MoneyBox 上游遷移規劃 - #1050

Open
s123104 wants to merge 10 commits into
mainfrom
docs/api-semantics-v3-prd
Open

docs(ratewise): 新增匯率 API 語意 v3 PRD 與 MoneyBox 上游遷移規劃#1050
s123104 wants to merge 10 commits into
mainfrom
docs/api-semantics-v3-prd

Conversation

@s123104

@s123104 s123104 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

為什麼會有這份 PRD

MoneyBox 匯率抓取自 2026-08-22 起持續失敗。原本判定是「上游停供 sell 欄位」,用瀏覽器實際載入官網並攔截網路請求後才發現:官網已遷移到自有 endpoint,完全不再呼叫舊 API。

GET https://moneybox-exchange.com/api/rates           → 200  ← 官網現在用這個
     https://cems.moneybox.or.kr/api/cmd.php?cmd=C011  → 完全沒有被呼叫

舊 endpoint 半殘base 持續更新、sell 恆為 0),所以純 API 探測分不出「上游壞掉」與「上游搬家」。這是本次診斷必須動用瀏覽器的原因。

順帶挖出的更大問題

調查遷移方案時發現 API Semantics v2 的命名方案已失效,且從未真正落地

1. 同名反運算 — 同一個欄位 customerBuyForeignRate

"twdToForeignFormula": "amount / details.{TO}.{rateType}.sell"   // bank:除
"twdToForeignFormula": "amount * rates.TWD.sell"                 // exchange-shop:乘

消費端沒同時讀 quoteUnit 就會算出倒數。這個欄位的整個賣點是「跨 provider 可比較」,但兩者連運算方向都不同。

2. 守門測試把錯誤認證為正確 — 7 條 v2 測試全在驗欄位對應,其中一條甚至把公式字串 'amount * rates.TWD.sell' 當期望值鎖住。沒有任何一條真的執行換算並檢查數值。

3. v2 從未真正落地 — 覆蓋率與流量成反比:

檔案 月流量 v2 語意層
台銀主匯率 latest.json 69,487(92%) 完全沒有
moneybox latest.json 6,100 有完整 v2 欄位

六個權威來源的調研

產品 層級 方向表達 狀態欄位
Wise(最接近本產品) 消費級匯兌 source(send)/target(receive)
Stripe FX Quotes 支付 from_currencyto_currency lock_status
OANDA v20 交易 instrument(合併字串) status: tradeable
exchangerate-api v6(實測) 公共資料 base_code + rates map result: success
ECB SDMX(實測) 官方統計 CURRENCYCURRENCY_DENOM OBS_STATUS(19 碼)
schema.org 語意標註 currencypriceCurrency

五家中四家用「兩個幣別欄位」表達方向;唯一例外 OANDA 是交易 API。本產品是消費級匯兌工具,應對齊 Wise/Stripe 一側。

由權威來源直接決定的三件事

  • rateType 是一級平行維度 — ECB 把 EXR_TYPE 建模成 series 維度;ISO 20022 將 SPOT 定為標準碼
  • status 直接採用 ECB CL_OBS_STATUS 官方碼表子集,不自創字串。其中 H(holiday or weekend) 正好對應換錢所休市、Q(suppressed) 對應 8/22 那場停供事故
  • 歷史檔缺 publishedAt 不得回填 — 官方統計的做法是保留觀測值並標註狀態,不用相鄰欄位推導填補

一個被權威推翻的自家設計

草案原本用 status: "normalized" 標示 per-100 正規化。ECB 對「單位」另有 UNIT_MULT 屬性,OBS_STATUS 分開建模 —— 狀態碼描述資料品質,不描述單位換算。已改為由 rateUnit 自我描述。

版本治理

標準 對本案的意義
Google AIP-180 「改名等同於移除+新增」,同 major 內不得移除或改語意 ⇒ 本案必須是 major,3.0 是正確載體
RFC 9745 / 8594 Deprecation / Sunset header 有標準格式,不應自創
Zalando #185/#186 關閉前必須取得客戶同意 —— 本案無法達成
Zalando #188 必須監控已棄用 API 使用量 —— 這是無法取得同意時的正當替代,而 jsDelivr 提供逐檔流量統計

產品裁決不留 sunset 期,但仍採標準做法降低風險:切換期間由既有的 security-headers Worker 注入 Deprecation/Sunset/Link header、openapi.jsondeprecated: true、以 jsDelivr 流量監控舊路徑。

一個有意思的對比

seo-metadata/core.ts 的幣別頁 JSON-LD 已經在用:

'@type': 'ExchangeRateSpecification',
currency: fromCurrency,
currentExchangeRate: { price: String(rate), priceCurrency: toCurrency },

正是 from/to 模型,不需要任何 flag。本專案的 SEO 層在語意上比資料 API 層更正確。 v3 的目標之一因此是讓 JSON-LD 成為資料模型的直接投影而非翻譯。

規劃

三段式 PR(線性依賴):

  1. ingest 吸收上游遷移 — 對外零變化,先讓 CI 恢復
  2. v3 canonical model + 419 檔歷史轉換 — 尚不切換公開入口
  3. 硬切公開產物與文件 — 唯一 breaking release,需原子化發布

PR 3 前置條件:app 目前仍讀 MoneyBox legacy getSellRate,硬切前必須先改讀 v3 並驗證換算等價,否則切換當下前端會取不到值。

含 13 項風險表、完整影響面清單(419 檔歷史 + 17 個 pair + 5 個產生器)、守門測試設計與回滾策略。

本 PR 內容

純文件:docs/dev/049_*.md(449 行)+ 002 記錄兩筆(1 reward / 1 penalty)。不含任何程式碼變更。

002 記分表頭我一開始算錯(1 reward + 1 penalty 寫成 delta +1),被 verify-002-log.mjs 守門攔下後修正。

🤖 Generated with Claude Code

- MoneyBox 官網已遷移至自有 endpoint,完全不再呼叫舊 cems API
- 舊 endpoint 半殘:base 續更但 18/20 幣別 sell 恆 0,純 API 探測無法分辨
- 以瀏覽器攔截網路請求確認新端點,三重驗證欄位反轉與 per-100 單位差
- 發現 v2 語意層同名反運算:bank 用除、shop 用乘,可比較性實質不成立
- 發現 v2 從未真正落地:佔 92% 流量的台銀主檔完全沒有語意層
- 以 ECB SDMX、Stripe、Wise、OANDA、schema.org、ISO 20022 六源設計 v3
- status 直接採用 ECB CL_OBS_STATUS 官方碼表子集,不自創字串
- 版本治理對齊 Google AIP-180 與 RFC 9745/8594、Zalando 指南
- 規劃三段式 PR:ingest 吸收、canonical model 與歷史轉換、硬切發佈

測試:prettier 通過;002 記分守門通過(表頭算術經守門攔截後修正)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fc95d46ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| `customerBuy.rate` | number | `received = paid × rate` | 必填 | schema.org `UnitPriceSpecification.price` |
| `customerBuy.rateUnit` | `"TO_PER_1_FROM"` | 固定值,明示「每 1 單位來源幣換多少目標幣」 | 必填 | FX `CCY1/CCY2 = X` 恆 per-1 |
| `customerSell.*` | 同上 | 反方向 | 必填 | — |
| `spread` | number \| null | `customerSell.rate − customerBuy.rate`(同向表示) | 無法計算時 `null` | schema.org `exchangeRateSpread` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 spread 的兩個操作數統一為相同報價方向

customerBuycustomerSell 在上一列被定義為互相反向,因此兩者的 rate 分別具有「目標幣/來源幣」與其倒數的不同單位,不能直接相減;例如 TWD→USD 約為 0.03,而 USD→TWD 約為 30,目前公式會產生約 29.97 而非實際價差。若依此 PRD 實作,對外的 spread 將沒有有效金融語意,應先把兩側正規化為同一 from/to 方向再計算。

Useful? React with 👍 / 👎.

Comment on lines +299 to +300
| **2** | v3 canonical model + 全歷史轉換(419 檔 + manifest) | **尚不**切換公開入口 |
| **3** | v3 硬切公開產物與文件 | **唯一對外 breaking release,需原子化發布** |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 避免 PR 2 原地改寫仍在公開服務的歷史端點

PR 2 同時被要求轉換現有 public/rates/history/*.json 與 MoneyBox history,這些路徑正是 OpenAPI 建議透過 cdn.jsdelivr.net/...@data 存取的公開端點;只要 PR 2 合併到 data 分支,既有 URL 就已回傳 v3,而 app、文件與其他公開產物要到 PR 3 才切換,因此「尚不切換公開入口」及「PR 3 是唯一 breaking release」無法成立。應讓 PR 2 寫入不會被既有 URL 命中的暫存/版本化路徑,並在 PR 3 才原子切換。

Useful? React with 👍 / 👎.

Comment on lines +248 to +250
| `status` | ECB `CL_OBS_STATUS` 碼(見 §4.4) | **每筆值自帶狀態碼**,說明缺值或品質;禁止用 `null` 代表語意 | 必填 | **ECB `CL_OBS_STATUS`** |
| `observedAt` | ISO string | **我方抓取時間** | 必填 | ECB series 屬性 `COLLECTION` |
| `publishedAt` | ISO string \| null | **上游宣告的發布時間**;缺值時**不得回填**,以 `status` 說明 | 上游未提供 `null` | ECB `TIME_PERIOD` 與屬性分離 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 為各個可缺失值提供獨立的狀態碼

一個 rate row 同時包含正常的 customerBuycustomerSell、可缺失的 referenceRate,以及可缺失的 publishedAt,但這裡只配置一個 status;例如 MoneyBox 報價正常、reference rate 不存在且發布時間未取得時,單一欄位無法同時表達報價為 A、reference 為 M、時間為 L。這也與「每筆值自帶狀態碼」的定義不符,會迫使消費者把正常報價視為 missing,或失去缺值原因,應將狀態附在各個值上或拆分成欄位專屬狀態。

Useful? React with 👍 / 👎.

| 欄位 | 定義 | 依據 |
| ------------------------------------------- | --------------------------------------- | --------------------------------------- |
| `schemaVersion` | `"3.0"` | Google AIP-180:語意變更須 major 版本 |
| `nextUpdateAt` | 下次排程更新時間(本專案排程每 5 分鐘) | exchangerate-api `time_next_update_utc` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 不要把排程間隔宣告為可靠的 nextUpdateAt

目前兩條匯率 workflow 的 YAML 都明載 GitHub schedule 不保證準點,且實際 cron 分別錯開在每小時的 :02/:07/...:04/:09/...;因此由「每 5 分鐘」直接產生 nextUpdateAt 會向消費者承諾一個可能尚未執行、甚至被延遲或漏掉的更新時間,依此降低輪詢頻率反而可能延長 stale data。應把它定義為非保證的下一次檢查提示、容許延遲,或只在有權威排程狀態時輸出。文檔任務必須與 repo 實際設定校對一致。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

Comment on lines +415 to +417
- [ ] `openapi.json` 舊 schema 標 `deprecated: true`(Zalando #187)
- [ ] `security-headers` Worker 於舊路徑注入 `Deprecation` / `Sunset` / `Link` header(RFC 9745 / 8594)
- [ ] 切換後以 jsDelivr 逐檔流量監控舊路徑是否仍有拉取(Zalando #188)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 為 CDN 端點改用可實際送出的棄用訊號

OpenAPI 將 cdn.jsdelivr.net/gh/haotool/app@data 與 GitHub Raw 列為 /public/rates/... 的服務端點,但 security-headers/wrangler.jsonc 的 Worker routes 僅涵蓋 *.haotool.org,因此它無法在主要舊端點的回應上注入這些 header;而原地硬切相同 URL 後也不存在可單獨標記的舊路徑。若此項是驗收條件,需先提供由該 Worker 控制的版本化/代理端點,否則應改採 CDN 可承載的頻內版本訊號。文檔任務必須與 repo 實際設定校對一致。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

- 實測 Wise Comparison API live 回應,16 家 provider 完整結構
- 關鍵發現:整份 payload 無任何 buy/sell/bid/ask 側名欄位
- 視角改由 provider type 分類承載,方向置於頂層查詢屬性
- receivedAmount 直接給答案,消費端不需自行做四則運算
- 實測顯示只看 rate 會誤判:nationwide 匯率較好但 fee 15 實得最差
- 採集時間相差 21 小時仍並列,靠逐筆 dateCollected 交付判斷
- 自提 8 條原則經獨立審查後僅 3 條完全成立,5 條前提未滿足
- 裁決中價不升格為 referenceRate,另立 comparisonBenchmark
- 靜態 CDN 架構下 receivedAmount 不可行,改 grossReceivedAmount

測試:prettier 通過;002 記分守門通過(計數經守門攔截後修正)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@s123104

s123104 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

補充 §13:權威匯率聚合器設計

追加調研「銀行視角 vs 換錢所視角買賣價」該如何設計,結論推翻了問題本身的前提

實測 Wise Comparison API(16 家 provider)

GET api.wise.com/v4/comparisons?sourceCurrency=GBP&targetCurrency=EUR&sendAmount=1000

整份 payload 沒有任何 buy / sell / bid / ask 欄位。 視角問題被四個機制取代:

機制 欄位
provider 分類 type: "bank" | "moneyTransferProvider"
方向在頂層 sourceCurrency / targetCurrency
金額語意 amountType: "SEND"
直接給答案 receivedAmount

兩筆實測數據值得記錄:

provider rate fee receivedAmount dateCollected
barclays 1.13594 0.0 1135.94 08-25T13:13
nationwide 1.14100 15.0 1123.88 08-26T10:20
  • 只看 rate 會得出錯誤結論:nationwide 匯率較好但實得最差
  • 採集時間相差 21 小時仍並列呈現,靠逐筆 dateCollected 交付判斷,不隱藏也不強行對齊

自提原則經獨立審查後大幅收斂

我從 Wise 提取了 8 條設計原則,送交獨立審查後僅 3 條完全成立(方向為查詢屬性、amountType 消除歧義、fee 與 rate 分離)。

其餘 5 條前提未滿足,例如:

  • receivedAmount 依賴 query 帶金額,本專案是靜態預生成 CDN JSON
  • markup 需可信中價基準,本專案目前無
  • Wise 的 isConsideredMidMarketRate 標的是自己;第三方聚合器需另外說明基準來源與中立性

四項裁決

  1. FIBO Party/Role 與 Wise 分類互補 — 採 provider.kind + 最小 providerRole,不導入完整 FIBO 本體
  2. cash/spot 與 provider.kind 正交 — MoneyBox 未經驗證前填 unspecified,不得推測填 cash
  3. open.er-api.com 中價不升格為 referenceRate — 另立 comparisonBenchmark(含 methodVersion/status/collectedAt)。升格即隱含本產品為該數字背書,而其採集時間與台銀/換錢所不一致、權威性未經驗證
  4. receivedAmount 靜態架構下不可行 — 短期改 grossReceivedAmount + 明示 feeStatus: "not_included"

對本 PR 的影響

§13 不阻擋 §5 的三段式遷移 —— v3 底座可先落地,聚合器能力分四階段推進(v3 底座 → 公開方法論 → markup 排名 → 動態 /comparisons API)。

PRD 由 449 行增至 537 行。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93fc876d61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+333

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 修正 002 檔頭的新增條目計數

此提交實際新增 2 筆 reward-* 與 1 筆 penalty-*,但檔頭宣告為 reward 1、penalty 0;以本提交父版執行 node scripts/verify-002-log.mjs --base-commit 83898869^ 會直接回報計數不符,導致 Quality Checks 失敗。請將計數改為 reward 2、penalty 1、neutral 0(淨變化與累計仍為 +1/+333)。

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.


---

## 6. 完整影響面清單

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 在硬切前遷移 Split Meow 的 MoneyBox 消費端

這份「完整影響面清單」漏掉了 repo 內另一個正式消費端:apps/split-meow/src/lib/exchangeRate.ts:1-40 直接請求同一個 MoneyBox CDN URL,並要求 rates.TWD.selltimestampupdateTime。PR 3 依本規劃把該 URL 原地切成 v3 後,該函式會固定拋出錯誤,讓 Split Meow 的 TWD/KRW 換算無法取得新匯率;請將此 app 的解析器與回歸測試納入 PR 3 前置條件。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.


| 欄位 | 型別 | 定義 | 缺值行為 | 權威依據 |
| -------------------------- | --------------------------------- | ------------------------------------------------------------ | ----------------- | ------------------------------------------- |
| `rateType` | `"cash"` \| `"spot"` | **一級平行維度**,非巢狀於 rate 之下(受控詞彙) | 必填 | ECB `EXR_TYPE`;ISO 20022 SPOT |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 讓 rateType 可合法表示 MoneyBox 的 unspecified

這裡把必填的 rateType 限定為 "cash" | "spot",但同一份已裁決文件在 §13.3 明定 MoneyBox 未經驗證前必須填 unspecified、不得推測為 cash。依目前 schema 實作時,所有 MoneyBox row 不是違反型別,就是被迫填入文件明令禁止的假語意;請把 unspecified 納入受控詞彙或在落地 v3 前完成並記錄 rate type 驗證。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.


- [ ] 19 幣別 fixture 驗證 `buyRate→legacy sell`、`sellRate→legacy buy`
- [ ] JPY/IDR/VND 專項單位測試
- [ ] 切換前後 v2 JSON 逐欄位快照比對,證明**對外值零變化**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 先定義缺失 legacy 欄位再要求逐欄零變化

新上游在 §2.4 已確認不再提供 basespbuyspsell,而現行公開 v2 row 仍由 scripts/fetch-moneybox-rates.js:141-147 輸出這三個欄位;因此 PR 1 只靠新 endpoint 無法通過這項「逐欄位快照比對/對外值零變化」驗收。若未先指定保留來源或相容策略,實作只能刪除、清空或沿用過期值;請明確裁決這三欄在 PR 1 的來源與缺值行為,或把相應 schema 變更移至 breaking PR 3。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

- amountTiers 改為納入:seo-paths 早有預生成金額階梯,靜態架構非阻礙
- grossReceivedAmount 否決:會把 rate 層的誤導原樣搬到 amount 層
- 費用未知時 allInReceivedAmount 必須為 null,禁止任何最佳推薦標籤
- comparisonBenchmark 延後:只有一組可比對時邊際效益不敵可信度風險
- 實測證實台銀與換錢所計價基準不同,16 個代碼交集實際不可比
- 真正可比的只有 TWD 對 KRW 一組,comparablePairs 升為強制顯著欄位
- 新增產品定位界定章節,明確排除四項超出實際覆蓋範圍的宣稱
- 新增十二項尚未對齊最佳實踐的清單,實作前須逐項收斂

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0333383ccf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+0(reward 1、penalty 1、neutral 0)|累計總分:+333

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 修正 002 檔頭的聚合計數

相較既有留言,本次聚合提交的新證據是:由父版到此提交實際新增 3 筆 reward-* 與 2 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 1、淨變化 0;執行 node scripts/verify-002-log.mjs --base-commit 39049932^ 會因計數、淨變化及總分鏈均不符而失敗,阻擋 Quality Checks。此處應記為 +1(reward 3、penalty 2、neutral 0),累計總分維持 +333

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

| `customerBuy.rateUnit` | `"TO_PER_1_FROM"` | 固定值,明示「每 1 單位來源幣換多少目標幣」 | 必填 | FX `CCY1/CCY2 = X` 恆 per-1 |
| `customerSell.*` | 同上 | 反方向 | 必填 | — |
| `spread` | number \| null | `customerSell.rate − customerBuy.rate`(同向表示) | 無法計算時 `null` | schema.org `exchangeRateSpread` |
| `referenceRate` | number \| null | 外部市場基準價 | 上游未提供 `null` | Stripe `reference_rate` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 為 referenceRate 指定方向與單位

當一個 rate row 同時包含互為反向的 customerBuycustomerSell 時,裸數字 referenceRate 沒有 fromCurrencytoCurrencyrateUnit,因此同一基準的約 30 TWD/USD 與其倒數約 0.033 USD/TWD 都符合目前契約;referenceRateProvider 只能說明來源,無法消除此歧義。消費者據此計算比較或 markup 時可能使用倒數值,應讓基準價攜帶方向與單位,或明確且可機械驗證地綁定其中一個方向。

Useful? React with 👍 / 👎.


## 13. 權威匯率聚合器設計(延伸議題)

> 本節回答「如何成為權威匯率聚合網站 API」。結論影響 v3 的**擴充方向**,但**不阻擋** §5 的三段式遷移——v3 底座(固定方向、per-1、固定乘法)可先落地。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 先收斂遷移必要規格再宣告不阻擋

若實作者依此處直接開始 §5 的 PR 2,§15 仍明列「實作前須逐項收斂」的台銀倒數精度/捨入規格與歷史 manifest 格式,而這兩項分別直接影響 PR 2 的轉換數值、hash 與 §3.4 要求的可回滾產物,並非僅是聚合器擴充議題。這個「不阻擋」宣告會允許不同實作者產生不可重現的歷史資料;應先裁決這些遷移必要項目,或明確將 PR 2 設為受其阻擋。

Useful? React with 👍 / 👎.

- 查證台銀與換錢所皆免手續費,價差即全部成本
- 推翻 feeCoverage 恆為 unknown 的前提,改 fee 0 加 pricingScope
- pricingScope 宣告牌告匯率邊界,排除線上優惠匯率與旅行支票
- 新增 comparisonProfile 取代 rateType 字串比對判可比性
- 原提案會錯殺唯一可比對:台銀 cash 對換錢所 unspecified
- 覆蓋範圍與可得性分離,避免 comparablePairs 隨抓取結果閃爍
- 十二項逐項裁決:5 項採納、7 項修正
- 另列 8 項續議,明確非已對齊
- 阻塞分析確認僅 amount tiers 與其設定可延後,PR 1 可立即開始

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1703d3011

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| 8 | 資料完整性失敗策略 | **修正** | 缺報價保留 provider + `CL_OBS_STATUS`;但 `comparablePairs` **不因單次缺報價被移除**,改輸出 `comparisonAvailability: "unavailable"` |
| 9 | 精度與捨入 | **修正** | ingest 只做一次倒數(採納),但「固定 8 位有效數字」**不足**——需 decimal 算術 + 明確 amount tier 捨入規則 |
| 10 | manifest 格式 | 採納 | JSONL,另補 `manifestVersion` / hash algorithm / `migrationId` |
| 11 | CDN 原子發佈 | **修正** | pointer 須指向**具 hash 的完整 release manifest**,涵蓋 latest/pairs/history/OpenAPI/文件為同一 release;purge 後**全面驗證**而非單檔抽查 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將排程資料更新納入原子 release

在 PR 3 完成初次切換後,目前檢查到的 .github/workflows/update-latest-rates.ymlupdate-moneybox-rates.yml 仍分別於每小時的 :02/:07/...:04/:09/... 獨立改寫並推送 latest/history 檔,而 update-historical-rates.yml 也會每日另行更新歷史資料;這些 workflow 未被 §6 的完整影響面或 PR 3 工作納入,也不會同步重建此處含 hash 的 manifest/pointer,因此發布後數分鐘內 manifest 就可能與實際檔案不符,重新形成混合 release。請讓這些管線共同發布不可變版本並原子推進 pointer,或明確排除會持續變動的路徑。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+334

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭對齊六筆新增條目

相較既有留言,本次以指定提交父版重新執行守門得到的新證據是:此提交實際新增 4 筆 reward-* 與 2 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 0;node scripts/verify-002-log.mjs --base-commit cb0af1e^ 會同時因計數、淨變化與總分鏈失敗而阻擋 Quality Checks。此處應記為 +2(reward 4、penalty 2、neutral 0)|累計總分:+334

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

| 不是 | 原因 |
| ---------------------- | -------------------------------------------------------- |
| 全球匯率比較服務 | 只有 TWD↔KRW 一組可比對(§13.3.4) |
| all-in 實得金額保證 | 無費用資料,`allInReceivedAmount` 恆為 `null`(§13.3.1) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 統一 allInReceivedAmount 的最終契約

實作者依此處會把 allInReceivedAmount 永久設為 null,但同一份 v7 文件稍後的 §15.1 明確推翻「無費用資料」前提,要求 fee.amount = 0coverage = complete,§15.3 又將該契約列為採納;這會讓 amount tier 產生器與 schema 對同一筆報價得到互斥結果。請更新此處及 §13.3.1 的舊結論,明確裁決在 pricingScope 相符時是否計算 all-in 金額。

Useful? React with 👍 / 👎.

Comment on lines +671 to +673
**只有 #4 #5 可延後**;其餘貫穿三個 PR。

> **PR 1 可以立刻開始**——它只需要 #7 #8 的 freshness 與缺報價狀態設計,兩者已於 §4.4 與 §15.3 定案。CI 的修復不被聚合器議題阻塞。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 將續議項目分配至實作階段

在 §15.4 剛列出 #13#20 尚未對齊後,這裡卻宣告除 #4/#5 外全部貫穿三個 PR,但上方實作表只分配了 #1#12#20,完全沒有 #13#19;其中 comparisonProfile schema、捨入規格及 methodVersion 又直接影響 PR 2/3 的輸出。照目前路線執行會讓這些被宣告不可延後的 blocker 無負責 PR,請逐項分配或明確標示哪些階段會被它們阻擋。

Useful? React with 👍 / 👎.

- 產品定位揭露:主打實際牌告買賣價,非其他工具的中間價
- 差異化模型早已實作於 seo-rate-examples,但從未進入公開 API
- 推翻中間價 deferred 裁決:它是對照組不是權威基準
- 中間價改名 marketMidCounterfactual,帶 purpose comparison_only
- 與上游明示的 providerReferenceRate 嚴格區分,不得混用
- 主匯率數值改名 publishedRate,傳達牌告實際成交價
- 新增 quoteNature 與 quoteAvailability 平衡精準與不過度承諾
- 精準度落差升為一級欄位,但只依附固定金額的 amountTiers
- 歷史期間無共時中價時 gap 必須為 null,不得用今日中價回填
- 誠實列出 6 項需外部資訊才能定案的項目與暫定行為

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e538e65c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| **2** | v3 canonical model + 全歷史轉換(419 檔 + manifest) | **尚不**切換公開入口 |
| **3** | v3 硬切公開產物與文件 | **唯一對外 breaking release,需原子化發布** |

> **PR 3 的前置條件(Codex 特別提醒)**:app 目前仍讀 MoneyBox legacy `getSellRate`。硬切前**必須**先改讀 v3 `customerBuy` 並驗證換算結果等價,否則硬切當下前端會取不到值。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將台銀主站解析器納入 PR 3

PR 3 將 public/rates/latest.json 從數值 map 硬切為 rates[],但這個前置條件只處理 MoneyBox;目前 apps/ratewise/src/services/exchangeRateService.ts:66-72 仍要求 rates 的每個值皆為正數,收到 v3 row 物件後會判定 payload 無效,使 RateWise 主站永久退回 build-time 舊匯率。請將此解析器及相應歷史服務的 v3 遷移與回歸測試一併列為公開硬切前置條件。文檔任務必須與 repo 實際設定校對一致。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+0(reward 1、penalty 1、neutral 0)|累計總分:+334

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭對齊八筆新增條目

相較既有留言,本次以指定提交父版重驗得到的新證據是:此提交實際新增 5 筆 reward-* 與 3 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 1、淨變化 0;執行 node scripts/verify-002-log.mjs --base-commit c2e99087^ 會因計數、淨變化及總分鏈不符而失敗,直接阻擋 Quality Checks。此處應記為 +2(reward 5、penalty 3、neutral 0)|累計總分:+334

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

- 端點探查確認僅有 api/rates 一個,每列僅三個欄位
- providerReferenceRate 對 MoneyBox 恆為 null,屬結構性事實
- 上游標頭自宣 max-age 14400 且提供 last-modified
- 我方每 5 分鐘輪詢為上游自宣 TTL 的 48 倍,改依快取契約排程
- nextUpdateAt 改名 nextSourceCheckAt,不承諾上游必然更新
- 新增 derivedQuoteMidpoint 但強制標記 isMarketRate false
- 推導中點不可取代外部市場中價,兩者用途不同須並存
- 外部市場中價授權阻塞未解除,未確認前為 null 且不得頂替
- decimal string 與十進位算術定案,禁止 binary float 進公開產物
- 區分 unspecified 與 null:前者有概念無資料,後者概念不適用

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ad738dda6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+0(reward 1、penalty 1、neutral 0)|累計總分:+334

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭對齊十筆新增條目

相較既有留言,本次以待審聚合提交的父版重新驗證後,新證據是實際新增 6 筆 reward-* 與 4 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 1、淨變化 0;執行 node scripts/verify-002-log.mjs --base-commit 3fd5b5f^ 會因計數、淨變化及總分鏈不符而阻擋 Quality Checks。此處應記為 +2(reward 6、penalty 4、neutral 0)|累計總分:+334

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.


| 項目 | 規格 |
| -------------- | --------------------------------------------------------------- |
| 公開 JSON 表示 | **decimal string**(`"42.3"` 而非 `42.3`) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 統一 publishedRate 的最終名稱與 JSON 型別

依 v9 實作公開契約時,此處要求所有公開小數使用 decimal string,但 §16.4 的「最終」範例仍輸出數字型別的 publishedRate: 42.15,§4.3 更仍定義舊名稱 customerBuy.ratenumber;OpenAPI、產生器與消費端若分別依循不同章節,會得到 ratepublishedRate 以及 number/string 互斥的 schema。請同步更新欄位表、範例與守門規格,使 v3 只有一種可實作的表示。

Useful? React with 👍 / 👎.


| 欄位 | 定義 | 標記 |
| ------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------- |
| `derivedQuoteMidpoint` | 同一 provider 兩側牌告價的**數學中點** | `isMarketRate: false`、`derivation: "arithmetic_mean_of_published_two_sided_rates"` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 先統一報價方向再計算 derivedQuoteMidpoint

相較既有的 spread 留言,新的證據是 v9 又新增 derivedQuoteMidpoint,卻未指定其輸入是在方向正規化之前或之後,也沒有為輸出附上 from/to 與單位;依 §4.3 的 canonical model,customerBuycustomerSell 是反方向,直接平均會把某方向的匯率與其倒數相加。若預期使用 MoneyBox 原始同向的 buyRatesellRate,也必須明定推導發生在反轉前並讓結果攜帶方向,否則實作者可能發布沒有金融語意的中點。

Useful? React with 👍 / 👎.

- 語意定義散落六個表面且已證實漂移
- llms.txt 專為 LLM 的欄位說明是散文寫死 v2,v3 上線即過期
- AI agent 讀到後照錯欄位名解析且無從察覺,比人類看 UI 更危險
- 定案以版本化 JSON Schema contract 為 SSOT
- TS 型別與 OpenAPI 與 llms.txt 與欄位表與驗證器全部由其衍生
- v3 移除 semanticFieldMapping,改以 schema 欄位指向 canonical contract
- llms.txt 散文與 schema 連結兩者並存,散文只留操作指引與禁止事項
- 匯率資料不需要 Markdown 表面,JSON 為 canonical data surface
- JSON-LD 改為已驗證 payload 的投影,不得自行讀 legacy 或重算
- schema.org 表達力不足者留在 canonical JSON,不得塞進誤導欄位
- 金額階梯設定分離但計算核心共用,CI 斷言禁止跨層 import
- 誠實列出十一項尚未達成,最大缺口是 contract 檔與生成鏈皆未建立

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0fea1b1188

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+0(reward 1、penalty 1、neutral 0)|累計總分:+334

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭對齊十二筆新增條目

相較既有留言,本次以待審提交父版重新驗證後的新證據是:此提交實際新增 7 筆 reward-* 與 5 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 1、淨變化 0;執行 node scripts/verify-002-log.mjs --base-commit 3a0dbd160d72606254cd4deeb071906df7f57d0d 會因計數、淨變化及總分鏈不符而阻擋 Quality Checks。此處應記為 +2(reward 7、penalty 5、neutral 0)|累計總分:+334

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

Comment on lines +790 to +792
我方 cron 每 5 分鐘(288 次/日),是上游自宣 TTL 的 **48 倍**;實測平日僅 25–34 次實際變動。

**裁決:停用每 5 分鐘輪詢,改依 `max-age=14400` 排程,並帶 `If-Modified-Since` 條件式請求。**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 不要把 HTTP 快取期限當成上游更新週期

相較先前僅針對 nextUpdateAt 的留言,本版新增的風險是直接裁決停用五分鐘輪詢並改成每四小時抓取;但上一段自己的量測顯示平日有 25–34 次實際變動,改成每日僅六次檢查會漏掉大多數牌告更新,讓公開 latest 最差落後近四小時。max-age=14400 只表示該回應可被快取多久,不代表來源四小時內不會更新或不會主動 purge;應保留足以偵測變動的檢查頻率並使用條件式請求降低成本,或先取得明確的上游更新 SLA 再降頻。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

- PRD 在後續章節多次改名,卻未回填 4.3 的原始欄位表
- 同一份文件因此存在兩套規範性欄位名,照前段實作會用到被推翻的名字
- 這正是本 PRD 整份在防的同名不同義問題,出現在自己身上
- 4.3 回填 publishedRate 與 marketMidCounterfactual 與 derivedQuoteMidpoint
- 4.3 補 quoteNature 與 quoteAvailability 兩個限定欄位
- 4.3.1 回填 schema 連結與 nextSourceCheckAt 與 calculationRule
- 新增 4.5 已汰換名稱對照表,含八組改名與原因
- 逐一驗證殘留出現處皆為對照或說明用途,無規範性殘留

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a71e6830a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


| 對象 | 處理 |
| ----------------------------- | ---------------------------------------------------- |
| 台銀 314 檔 | 只做結構/方向正規化,**數值不變**(已驗證皆 per-1) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 對台銀反向報價執行倒數轉換

台銀現行 TWD→外幣公式是 amount / details.{TO}.{rateType}.sellapps/ratewise/src/config/rate-mode-strategies.json:7),但 v3 不變式改為 received = paid × publishedRate;因此把約 32 TWD/USD 的舊值原封不動放入 customerBuy,會將 1,000 TWD 算成 32,000 USD,而不是約 31.25 USD。台銀資料雖已是 per-1,方向正規化仍必須對其中一側取倒數,故此處及 PR 2 的「數值不變」驗收應改成方向相依的轉換規則與驗證。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

| `customerBuy.quoteAvailability` | `"indicative_not_transaction_guarantee"` | **防止過度承諾**:牌告價會變動、現場可得性不保證 | 必填 | §14 排除清單 |
| `customerSell.*` | 同上 | 反方向 | 必填 | — |
| `spread` | decimal string \| null | `customerSell.publishedRate − customerBuy.publishedRate` | 無法計算時 `null` | schema.org `exchangeRateSpread` |
| `derivedQuoteMidpoint` | decimal string \| null | 同一 provider 兩側牌告價的數學中點;須帶 `isMarketRate: false` 與 `derivation` | 任一側缺報價 `null` | §18.3 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 將 derivedQuoteMidpoint 改為可承載中繼資料的型別

權威欄位表把 derivedQuoteMidpoint 定義為 decimal string | null,同一列卻要求它攜帶 isMarketRate: falsederivation;JSON scalar 無法包含這兩個欄位。依此表生成 contract 時只能捨棄必要的防誤用標記,或輸出不符合宣告型別的物件,因此應將它定義為含數值、方向/單位及上述中繼資料的物件,或把中繼資料拆成明確的同層欄位。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

Comment on lines +534 to +535
| `allInReceivedAmount` | number \| **null** | **費用未知時必須為 `null`** |
| `rateAppliedReceiveAmount` | number | 僅套用匯率的結果 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 將公開 amount tier 小數統一為 decimal string

這裡仍把 allInReceivedAmountrateAppliedReceiveAmount 宣告為 JSON number,但 §18.4 已要求公開 JSON 使用 decimal string 並禁止 binary float 進入公開產物。對具有小數 minor unit 的換算結果,產生器若依此表輸出 number,便會重新引入浮點序列化差異並使 schema、hash 與精度守門互相衝突;應將這兩個公開金額欄位改成 decimal string(或明確縮限 §18.4 的適用範圍)。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

- 確認五項漂移,其中兩項為我方自造
- 台銀 workflow 佔九成流量卻缺 outage routing,只修了小來源
- 人民幣 FAQ 推薦線上結匯最優惠,與我方排除該情境的宣告衝突
- 我方四項非漂移判定經審查後兩項被推翻
- 以上游 no-cache 推論輪詢頻率適當,是把未被禁止當成已被證成
- 十八種貨幣宣稱未區分已設定與可發布與可比較與當期可用
- 多幣別同時比較文案對讀者與 AI 易誤讀為跨 provider 比較
- 換錢所 FAQ 的現場可換金額可能超出牌告價能保證的範圍
- 產品定位章節因免手續費新事實需重新定稿,絕對表述已不成立
- 共識狀態為否,七項未決且其中五項需外部事實而非更多討論

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4471f6deb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:-1(reward 1、penalty 2、neutral 0)|累計總分:+332

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭對齊十六筆新增條目

相較既有留言,本次以指定提交父版重驗得到的新證據是:此提交實際新增 8 筆 reward-* 與 8 筆 penalty-*,但檔頭宣告 reward 1、penalty 2、淨變化 -1;執行 node scripts/verify-002-log.mjs --base-commit a2b8bbce0dc408cde852c31df4cebbb550ddfdb2^ 會因計數、淨變化及總分鏈不符而阻擋 Quality Checks。此處應記為 +0(reward 8、penalty 8、neutral 0)|累計總分:+332

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

- 初版以無費用資料為由排除 all-in 實得金額
- 該前提已被查證的免手續費事實推翻,價差即全部成本
- 實際上界定情境內算得出 all-in,限制不在算不出來
- 真正的分界是算得出成本結構不等於保證拿得到
- 新增是什麼章節,明示牌告換現鈔情境內可提供 all-in
- 新增現場成交保證排除,庫存與營業時間不在資料範圍
- 新增牌告情境外來源排除,線上結匯優惠匯率不適用
- 新增可以說與不可以說對照表,直接對應稽核發現的文案矛盾
- 條件式規則補交叉引用,避免誤讀為本專案費用未知

測試:prettier 通過;002 記分守門通過

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 251af6e10d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+5(reward 5、penalty 0、neutral 1)|累計總分:+332
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+333

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 將 002 檔頭計數對齊十七筆新增條目

相較既有留言,本次以指定提交父版重驗的新證據是:此提交實際新增 9 筆 reward-* 與 8 筆 penalty-*,但檔頭仍宣告 reward 1、penalty 0;執行 node scripts/verify-002-log.mjs --base-commit db9eec6^ 會因計數不符而阻擋 Quality Checks。請改為 +1(reward 9、penalty 8、neutral 0)|累計總分:+333

AGENTS.md reference: AGENTS.md:L203-L205

Useful? React with 👍 / 👎.

Comment on lines +343 to +344
- `public/rates/history/*.json`(**314 檔**)
- `public/rates/providers/moneybox/history/*.json`(**105 檔**)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 在原地轉換歷史檔前淘汰瀏覽器的 v2 快取

對曾瀏覽歷史資料的已安裝 PWA,apps/ratewise/src/sw.ts:409-443 會以 CacheFirst 將這些相同 URL 的回應保存一年;把歷史檔原地改成 v3 並 purge CDN 並不會清除使用者端 Cache Storage,因此新版解析器仍可能收到舊 v2 payload,破壞 PR 3 所宣稱的原子切換。請把 runtime cache 名稱版本化/在新 SW 啟用時刪除舊 cache,或改用版本化歷史 URL 並切換 pointer。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.


### 6.4 產生器

`generate-api-json.mjs`、`generate-pair-json.mjs`、`generate-openapi.mjs`、`generate-llms-txt.mjs`、`generate-history-aggregate.mjs`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 將 Markdown API 鏡像產生器納入 v3 遷移

這份產生器清單漏掉 build 必跑的 apps/ratewise/scripts/generate-markdown-mirrors.mjs;該檔目前仍在 412、420–421 行產生 .details.USD 的 v2 curl/JavaScript 範例,並輸出公開的 open-data.md。若 PR 3 只依此清單遷移,pnpm build 仍會重新生成無法解析 v3 rates[] 的公開文件,與「全部衍生產物一致」的驗收條件衝突。

AGENTS.md reference: AGENTS.md:L160-L164

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant