* feat(runtime): partial notify and diagnostics after scheduler timeout
After a hard timeout, scan already-saved analyses and enrich last_error
with completed/pending counts; optional push via DSA_TIMEOUT_PARTIAL_NOTIFY.
Refs #2328
* test(runtime): cover timeout partial delivery helpers
Refs #2328
* docs: document DSA_TIMEOUT_PARTIAL_NOTIFY
Refs #2328
* fix(config): use switch ui_control for timeout partial notify
DSA_TIMEOUT_PARTIAL_NOTIFY used ui_control=toggle, which SystemConfigResponse rejects and broke GET /config in backend-tests 1/3.
* docs(runtime): document timeout partial fail-open for operators
Channel exceptions are swallowed after the analysis lock is released, so they cannot keep status.running true. Collect/import failures stay in warning logs because last_error cannot distinguish them from zero completions.
* feat: add bot index analysis entry
* test: add Bot index online smoke
* fix: guard Windows-only spawn flag in smoke tests for Linux CI
* fix: clean up worker tree on parent interrupt in smoke runner
* fix: surface interrupt cleanup failures in smoke runner
---------
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
* feat: add minimal agent trajectory metrics with golden samples
* feat: add runnable agent trajectory eval entry with JSON and text reports
* fix: treat unsuccessful AgentResult as a run failure in trajectory eval
* fix: reject multi-arch executor and validate golden tools against the real registry
* Exclude litellm 1.99.0 from allowed versions
litellm 1.99.0 (released 2026-09-01T00:42:41Z) now forwards the
prompt_cache_key argument to the OpenAI provider. The provider cache
contract test asserts the key is not passed through without verified
capture, so CI backend-tests (3/3) and the downstream backend-gate
went red on every branch right after the release, unrelated to any
code change.
Local capture server reproduction: with 1.98.0 the captured request
body carries no prompt_cache_key; with 1.99.0 it carries
prompt_cache_key=cache-key. This one-line exclusion follows the
existing quarantine pattern for 1.82.7 / 1.82.8 and keeps the tested
behaviour until the contract is adapted deliberately.
* Bound litellm below the 1.99.0 behavioral change
Pin the dependency window to a verified upper bound (latest 1.98.x)
instead of quarantining only 1.99.0, and sync every doc that hardcodes
the litellm constraint string so docs cannot drift from requirements.txt
again.
* Sync .env.example runtime constraint comment to the litellm upper bound
* feat: make self-hosted SearXNG search timeout configurable
SEARXNG_TIMEOUT_SECONDS (default 10s, min 1) now controls the per-search
timeout for self-hosted SearXNG instances, threaded through all three
SearchService construction sites. Public-instance timeout stays fixed.
* fix: thread SEARXNG_TIMEOUT_SECONDS through subprocess rebuild and Actions
- persist searxng_timeout_seconds in _constructor_kwargs so the bounded
topic-news subprocess rebuild no longer silently falls back to 10s
- map SEARXNG_TIMEOUT_SECONDS (vars||secrets) in 00-daily-analysis.yml
- document the variable in full-guide/full-guide_EN data-source tables,
add CHANGELOG entry, and add regression tests for both paths
* fix: document SearXNG timeout environment contract
* fix: register SearXNG timeout setting
* fix: keep SearXNG timeout env-only
---------
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
Co-authored-by: zhulinsen <zhuls97@163.com>
The US daily-kline branch ignored fetcher priorities and used a fixed
Finnhub/AlphaVantage/Yfinance/Longbridge order, so per-source knobs
like YFINANCE_PRIORITY=0 never affected US routing. The chain is now
ordered by each fetcher's current priority (stable): defaults match
the builtin chain exactly, single-source *_PRIORITY tweaks take
effect immediately, and the deliberate first-position semantics
(index -> Yfinance, Longbridge-preferred) stay pinned.
* feat: add data capability contract
* fix(review-feedback-2289): preserve unknown status until availability is checked and Make
* fix(review-feedback-2289): Aggregate daily quality across supported markets and add kline
* fix(review-feedback-2289): Honor daily-source circuit breakers in quality selection and Make
* fix(review-feedback-2289): Scope market-overview quality by market and Do not select a news
* fix: align data capability with runtime routes
* fix: align data capability runtime coverage
* fix: align source and index capability routes
* fix: preserve runtime capability uncertainty
* fix: include US index capability routes
* fix: align realtime and monitor capabilities
* fix: remove unsupported Tushare index capability
* fix: align capabilities with runtime routes
* fix: model realtime and breaker routes
* fix: align US realtime request priority
* fix: align realtime circuit coverage
* fix: filter unavailable daily priorities
* fix: align US realtime capability claims
* fix: align executable US data routes
* feat: add one-shot index analysis via --stocks with structured AnalysisTarget pipeline
* fix: render index data sources in aggregate reports
* fix: refresh index registry before --stocks parsing and filter indices on CN holidays
---------
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
* feat: add a global desktop update entry in the shell
Expose a top-right DesktopUpdateIndicator in Electron runtime that
shares update state with Settings, without triggering extra background
checks or showing the control in ordinary browser WebUI.
Co-authored-by: SPEC <zt1y17@soton.ac.uk>
* fix: restore changelog header and honor desktop update deep links
Keep the Unreleased section intact, show download progress in the
shell tooltip, and open Settings version info from the search/hash.
Co-authored-by: SPEC <zt1y17@soton.ac.uk>
* fix: share desktop update checking state across header and settings
Keep a module-level update store so the shell indicator and settings
page disable together, and reject overlapping manual checks in the
Electron main process.
Co-authored-by: SPEC <zt1y17@soton.ac.uk>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* feat: add Futu OpenD as an optional HK realtime and fundamental data source
Add FutuFetcher and FutuFundamentalAdapter behind FUTU_OPEND_HOST/PORT,
register the settings in Config and config_registry so the Web settings
page can expose host, port and HK realtime priority, and route HK
realtime quotes through a configurable futu/longbridge/akshare/yfinance
order while keeping A-share priority untouched. Include offline tests
for the adapter, config schema and HK routing/fallback, plus docs and
CHANGELOG entries.
* fix: wire Futu fundamentals into HK pipeline and restore quote supplementation
- _fetch_offshore_fundamental_bundle() prefers the Futu fundamental
adapter for HK when FUTU_OPEND_HOST is configured, and falls back to
yfinance when Futu is absent or returns no usable content.
- HK realtime priority loop now supplements missing quote fields
(volume_ratio / turnover_rate / pe/pb / market cap) from later
configured sources instead of returning after the first non-empty
quote, matching the US path's _supplement_quote behavior.
- capital_flow / boards blocks are filled from the Futu bundle for HK
instead of being hard-coded not_supported; status and missing_fields
aggregation updated accordingly.
- Add regression tests for partial-quote supplementation and Futu
fundamental bundle routing/fallback.
* test: expect boards block ok when bundle provides belong_boards
The Futu integration made the offshore boards block data-driven instead
of hard-coded not_supported; update the existing US/HK fundamental
context test to match (belong_boards from the bundle now surface as an
ok boards block).
* fix: preserve HK fallback_from metadata and normalize Futu quote timestamps
- HK realtime priority loop now records the failed preferred source token
and passes it as fallback_from when a later source takes over, so the
pipeline and analysis context can mark the quote as degraded.
- Futu snapshot update_time is a naive Beijing-time (UTC+8) string; attach
the +08:00 offset before storing provider_timestamp so stale_seconds /
is_stale / provider_timestamp freshness semantics are correct instead of
being parsed as UTC.
- Add regression tests for fallback_from propagation and timestamp
normalization.
* fix: normalize Futu belong_boards to name/type/code contract
OpenD owner_plate returns plate_code / plate_name / plate_type, but DSA
downstream consumers (notification, extract_board_detail_fields, market
structure) only read name/type/code. Map the fields in
FutuFundamentalAdapter._boards so HK Futu boards are actually consumed
instead of silently dropped, and add regression tests including an
end-to-end check through extract_board_detail_fields.
* fix: merge yfinance bundle when Futu fundamental returns partial blocks
Futu partial success (e.g. statements failed but static info worked) used
to short-circuit the whole bundle, silently dropping the growth/earnings
that the existing yfinance path could still provide. Now, when Futu
returns content but is missing growth or earnings, fetch the yfinance
bundle within the remaining budget and merge the missing blocks
(growth/earnings/institution/capital_flow/belong_boards), keeping
Futu-preferred values where both exist. Add regression test for the
partial-success merge path.
* fix: use field-level checks when deciding Futu-vs-yfinance growth/earnings
The previous merge condition only checked dict truthiness, so a truthy
growth/earnings shell (all-None core values or metadata-only keys such
as report_date/period/currency) would skip the yfinance supplement and
silently downgrade existing HK fundamentals. Add _earnings_block_has_values
(a core numeric field or a populated dividend is required) and reuse the
existing _has_meaningful_payload for growth; both the missing_core check
and the merge loop now use these. Add regression test for the
all-None-shell scenario.
* fix: fill HK fundamental field gaps from yfinance instead of block-level checks
Block-level meaningful checks still skipped the yfinance supplement when
Futu hit only part of the growth/earnings fields (e.g. revenue_yoy but
None net_profit_yoy, or earnings with only basic_eps), silently dropping
fields the main branch used to provide. Replace the missing_core decision
with a per-field gap list (growth: revenue_yoy/net_profit_yoy/gross_margin;
earnings.financial_report: revenue/net_profit_parent/basic_eps/gross_profit)
and make the merge field-level: keep Futu values, fill each missing field
from yfinance. Add regression tests for partial-hit and all-None shells.
* fix: normalize Futu dividends to the repo contract and treat dividend gaps as supplement triggers
Futu OpenD dividend_list carries raw fields (statement/ex_date/record_date)
which the notification/data_processing market-structure consumers do not
read; the repo contract is ttm_cash_dividend_per_share,
ttm_dividend_yield_pct and events[].cash_dividend_per_share /
ex_dividend_date / event_date. Normalize events in
FutuFundamentalAdapter._dividends_and_splits, compute TTM count/cash and
yield from the latest quote, and teach _field_gaps/_merge_bundles to treat
a dividend block that does not satisfy the contract as a gap so yfinance
supplements it. Also dedupe FUTU_OPEND_HOST/PORT in full-guide_EN.
* fix: read dividend yield price from UnifiedRealtimeQuote objects
FutuFetcher.get_realtime_quote returns a UnifiedRealtimeQuote dataclass,
not a dict, so the yield branch in _dividends_and_splits that guarded on
isinstance(quote, dict) never ran on the live Futu path, silently dropping
ttm_dividend_yield_pct while the contract check considered the dividend
block complete. Read price via getattr(quote, 'price', None) and keep the
dict fallback for other fetchers; add a regression test driving the real
UnifiedRealtimeQuote shape.
* fix: treat dividend blocks with TTM cash but no yield as supplement gaps
The repo contract consumes ttm_cash_dividend_per_share and
ttm_dividend_yield_pct together. When the Futu dividend path has events
and TTM cash but the extra realtime price snapshot failed (quote None /
no price), ttm_dividend_yield_pct cannot be computed and the block was
previously treated as complete, so yfinance was never consulted and the
notification rendered the yield as N/A.
_dividend_contract_has_values() now requires the paired yield whenever
TTM cash is present, so _field_gaps() triggers the yfinance supplement
and _merge_bundles() replaces the incomplete dividend block.
Add regression tests for the adapter-level gap shape (quote unavailable
leaves no yield) and the manager-level supplement path (Futu cash
without yield pulls yfinance and fills the yield).
* fix: skip unconfigured Futu in HK realtime routing
When FUTU_OPEND_HOST is not configured, the HK realtime priority loop
used to still attempt the futu source, record it as the failed primary,
and attach fallback_from='futu' to a successful quote from the next
enabled source (longbridge/akshare/yfinance). Consumers then wrongly
treated an enabled source's first success as degraded fallback data,
contradicting the documented contract that Futu only participates when
OpenD is configured.
The HK loop now checks FutuFetcher.has_configured_endpoint() once and
skips the futu token entirely when it is disabled, so no fallback_from
is written. Existing configured-Futu routing tests explicitly patch the
endpoint check; a new regression test asserts an unconfigured Futu is
never called and the enriched quote carries fallback_from=None.
* fix: release cached HK Futu fundamental fetcher in DataFetcherManager.close()
The HK Futu fundamental path lazily creates and caches its own
FutuFetcher (an OpenQuoteContext-backed OpenD connection) on
_futu_fundamental_fetcher, but close() only released the TickFlow
fetcher and the default fetchers snapshot. Explicit close / reload
paths therefore left the OpenD connection hanging.
close() now takes the cached _futu_fundamental_fetcher, clears the
reference and calls its close() best-effort. A regression test injects
an observable fetcher into _futu_fundamental_fetcher and asserts
close() invokes it and clears the attribute.
---------
Co-authored-by: BayMax local review <baymax-local@invalid>
* ci: skip heavy checks for docs-only changes
* test: cover docs-only CI path filters
* fix: preserve CI checks for contract assets
* fix: preserve CI checks for license notices
* fix: render CJK text in Linux share images
* fix(review-feedback-2270): 补到 PR 附件或评论里,和本次渲染修复说明保持一致
* fix: preserve report language contract in share images
* feat: add canonical_id column and dual-write for stock daily data (#2207)
* fix: unify bare index codes to index canonical_id via matched_index (review OR-COR-4f9ffc38)
* docs: update docstrings for index-aware derivation and fix orphaned inspection-failure test (review OR-COR-4f9ffc38)
* fix: report actual market review backend
* fix: preserve exhausted fallback diagnostics
* fix(review-feedback-2241): update the attempt loop to retain the current model even when the and
* fix: preserve resolved market review provider
* fix(review-feedback-2241): Resolve aliased LiteLLM providers before recording
* fix(review-feedback-2241): preserve the failed LiteLLM route's resolved provider
* fix(review-feedback-2241): preserve template fallback for primary LiteLLM exhaustion and Use the
* fix(review-feedback-2241): preserve the fallback provider for unqualified response models
* fix(review-feedback-2241): Derive exhausted aliases from the router's last deployment and
* fix(review-feedback-2241): preserve the provider for gateway-owned slash model IDs
* fix(review-feedback-2241): preserve the explicit provider for unqualified failure models
* fix(review-feedback-2241): fix several earlier route-alias and router-failure diagnostics gaps,
* fix(review-feedback-2241): 评审结论 - 代码检查 :当前整个 PR 仍有 1 个未关闭的高置信度代码 blocker。最新复核摘要:Full
* fix(review-feedback-2241): 补一组成功路径回归:同一 alias 下首个 deployment 为 openai/~...、后续 deployment 为直连
* fix(review-feedback-2241): add a regression test that injects an analyzer exposing only is
* fix(review-feedback-2241): add a regression that asserts the legacy injected-analyzer path does
* fix(review-feedback-2241): 评审结论 - 代码检查 :当前整个 PR 仍有 1 个未关闭的高置信度代码 blocker。最新复核摘要:基于当前 HEAD
* fix(review-feedback-2241): 评审结论 - 代码检查 :当前整个 PR 仍有 1 个未关闭的高置信度代码 blocker。最新复核摘要:基于完整
* fix(review-feedback-2241): add a regression test that drives call litellm
* fix(issue-2201): [bug]-分析后无报告生成
* fix(review-feedback-2211): Keep the TTM cutoff anchored to the as-of date and Propagate report
* fix(review-feedback-2211): Move the empty-watchlist check before the trading-day return
* fix(review-feedback-2211): preserve the failure status, but defer returning until after the and
* fix(review-feedback-2211): preserve the failure result, but defer returning until after the
* fix(review-feedback-2211): Handle failures in the market-review-only CLI branch