* fix: surface event payload read/parse failures in ai_review (fixes#2070)
Issue #2070: .github/scripts/ai_review.py::_event_payload() previously
caught (OSError, ValueError) and silently returned {}, which collapsed
three distinct failure modes — missing event file, unreadable file, and
malformed JSON — into a single downstream symptom
('PR number is unavailable for GitHub API review'), making PR review
failures in workflow_dispatch / schedule runs impossible to triage.
Fix preserves the empty-payload degradation contract (so PR_NUMBER still
unblocks the chain when set explicitly), but splits the except clause
into three distinct branches that print a warning identifying the
failure mode by name (file-missing / OSError-derived / JSONDecodeError-
derived), the source path being GITHUB_EVENT_PATH, and the exception
class name. The warning never prints the payload content.
Tests in tests/test_ai_review_github_api.py add regression coverage for:
- missing event file -> {} + 'GITHUB_EVENT_PATH 指向的文件不存在'
- unreadable file (chmod 0o000) -> {} + '事件载荷读取失败' (skipped
on root runners where chmod is a no-op, but never raises)
- invalid JSON -> {} + '事件载荷 JSON 解析失败'
- valid JSON happy path -> payload + no warning (guards against the
warnings accidentally firing on success)
- PR_NUMBER unset + bad event payload -> RuntimeError surfaces with
the warning printed first so logs distinguish 'bad payload' vs
'no PR number'
10 tests pass (5 new + 5 existing) in 0.09s.
* fix: 回应 codex P2 review 反馈 (PR #2096)
1. UnicodeDecodeError 显式分支: open(..., encoding='utf-8') 在非合法 UTF-8
字节序列上抛 UnicodeDecodeError(是 ValueError 子类,旧 (OSError, ValueError)
接住了它,但拆成 OSError + JSONDecodeError 后该异常不再被覆盖,会让 review
终止而非降级). 新增 unicode 分支恢复降级行为,补 1 条独占回归测试.
2. CHANGELOG 收窄到本 PR 实际范围: 移除两条无关条目(parse_analysis_target 来自
PR #2094,YfinanceFetcher 港股裸码路由来自 PR #2097),它们不应进入本 PR 的
release notes.
* feat: support custom Tushare Pro endpoint via TUSHARE_HTTP_URL
Add TUSHARE_HTTP_URL so the Tushare data source can point at a self-hosted
or third-party compatible endpoint when the official api.tushare.pro is not
reachable. Defaults to the official host when unset, so behavior is
unchanged for existing users.
- data_provider/tushare_fetcher.py: add _resolve_tushare_http_url() helper
(strip + http(s):// schema validation) and forward the resolved URL into
_TushareHttpClient, with an info log when a custom endpoint is in use
- .env.example + .github/workflows/00-daily-analysis.yml: document and map
TUSHARE_HTTP_URL so the new option is wired into the daily job without
leaving a half-configured state
- tests: cover env parsing (empty/whitespace/http/https/missing schema),
fetcher fall-through to the official host, and end-to-end POST target
- docs/CHANGELOG.md: flat [Unreleased] entries
Fixes#1985
* refactor: drop unnecessary string-literal type hint in _build_api_client
TushareHttpClient is already defined above TushareFetcher in the module
scope, so a string-literal type hint is not needed for forward reference.
Restore the bare type to match the surrounding code style and reduce the
diff against main.
* docs(tushare): 补 TUSHARE_HTTP_URL 在 full-guide 中英版本的用途/默认行为/workflow 映射说明
按 PR #2048 review 反馈补齐:
- 表格内新增 TUSHARE_HTTP_URL 行(中英文版本同步),明确默认 https://api.tushare.pro 与 http(s):// 前缀要求
- 在 GitHub Actions 段落后补充 TUSHARE_HTTP_URL 的 vars/Secrets 优先级与每日 workflow 0映射说明,与现有非敏感配置(TICKFLOW_PRIORITY)一致
- 完整环境变量列表(中英文版本)补 TUSHARE_HTTP_URL 行,默认值列填 https://api.tushare.pro
- 与代码实现一致:00-daily-analysis.yml 已用 vars.TUSHARE_HTTP_URL || secrets.TUSHARE_HTTP_URL 映射
* docs(tushare): align TUSHARE_HTTP_URL default with runtime and clarify vars/secrets precedence
Per review feedback on PR #2048: the per-repo config contract must stay
consistent across runtime, .env.example, workflow priority, tests and
both zh/en guides. Two fixes applied as a single contract update:
1. Default endpoint alignment. data_provider/tushare_fetcher.py:100,
.env.example, and tests/test_tushare_fetcher_http_client.py all keep
the existing official endpoint http://api.tushare.pro, but the zh/en
full-guide rows had drifted to https://api.tushare.pro. Switching the
documented default to HTTPS would silently change the runtime contract
that the feat commit explicitly preserved. Revert both zh and en
guide rows to http://api.tushare.pro so docs match runtime, .env.example
and the test assertions.
2. vars/secrets precedence wording. The workflow uses
'vars.TUSHARE_HTTP_URL || secrets.TUSHARE_HTTP_URL', which means a
non-empty vars entry always wins and Secrets cannot override it. The
zh/en notes previously suggested 'Secrets as a tamper fallback' which
is incorrect under this precedence and can mislead users into thinking
Secrets has override power. Replace with explicit description of the
real semantics: vars wins when non-empty; Secrets is only selected
when the Variable is empty; for a tamper-resistant deployment put the
value only in Secrets and leave Variables empty.
Both zh and en guides are updated together; the same 6 contract surfaces
(runtime / .env.example / workflow priority / tests / zh guide / en guide)
now describe one consistent contract.
* docs(tushare): remove false Secret-as-tamper-guard claim, document real vars/secrets write-permission model
Introduce the StrategyEngine facade and centralize deterministic opinion partitioning, aggregation, synthesis, and signal normalization.
Keep invalid opinions in diagnostics, protect synthesis ownership across fallback paths, and add localized strategy rendering for notification, history, Markdown, and WeChat outputs.
Harden legacy and malformed strategy_synthesis payload handling through shared renderer helpers, with public-entry regression coverage and an updated Phase 1 contract.
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
get_realtime_quote hardcoded pe_ratio and pb_ratio to None for US stocks,
so the valuation block was always empty and every US quote reported
data_quality="partial" with pe_ratio/pb_ratio in missing_fields.
yfinance already returns both via Ticker.info (trailingPE / priceToBook),
and info is fetched a few lines above for the stock name, so this reads
them from the existing dict with no extra request.
The US index path (_get_us_index_realtime_quote) is intentionally left
alone: indices have no PE/PB.
Verified: AAPL now returns pe_ratio=39.67, pb_ratio=45.08 and
missing_fields shrinks from ['amount','pe_ratio','pb_ratio'] to ['amount'].
The stock_code parameter was described to the model as "A-share stock
code, e.g., '600519'". The agent reads that schema literally and does not
call get_stock_info for US/HK tickers, so any skill relying on
fundamentals silently degrades to a price/news-only answer there.
The handler already supports these markets: for AAPL it returns
market="us", status="ok" with valuation, growth (revenue_yoy,
net_profit_yoy, roe, gross_margin) and earnings (revenue,
net_profit_parent, operating_cash_flow) populated via the yfinance
fundamental adapter. Only the description was misleading.
Verified with the growth_quality skill on AAPL: before, the agent called
get_daily_history/analyze_trend/get_chip_distribution/search_stock_news
and never get_stock_info, producing a purely technical summary. After, it
calls get_stock_info and the report cites ROE 141.5% and net profit
+19.4% YoY.
* feat: add reliable home watchlist workspace
* fix(review-feedback-1984): Don't trust stale stock-bar rows after refresh failures
* fix(review-feedback-1984): apps/dsa-web/src/stores/stockPoolStore.ts 的 refreshStockBar
* fix(review-feedback-1984): Clear loading when refresh supersedes initial stock-bar load
* fix(review-feedback-1984): Type stock-bar test fixtures as StockBarItem and 跟进结论 - 结论 :不接受;最新
* fix(review-feedback-1984): tying the Today fetch to the same refresh path while this tab is